Snap for 6446396 from 7fc367463824cc8f98d5e50fa07460d5f251097e to studio-4.1-release

Change-Id: I330d8c89e609d9e791267bd1af549ece6d91fd49
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 abbbb9a..fa38157 100644
--- a/Android.bp
+++ b/Android.bp
@@ -30,10 +30,15 @@
     "src/trace_processor/metrics/android/android_package_list.sql",
     "src/trace_processor/metrics/android/android_powrails.sql",
     "src/trace_processor/metrics/android/android_startup.sql",
-    "src/trace_processor/metrics/android/android_startup_cpu.sql",
     "src/trace_processor/metrics/android/android_startup_launches.sql",
+    "src/trace_processor/metrics/android/android_task_names.sql",
     "src/trace_processor/metrics/android/android_task_state.sql",
+    "src/trace_processor/metrics/android/android_thread_time_in_state.sql",
+    "src/trace_processor/metrics/android/cpu_info.sql",
+    "src/trace_processor/metrics/android/display_metrics.sql",
     "src/trace_processor/metrics/android/heap_profile_callsites.sql",
+    "src/trace_processor/metrics/android/hsc_startups.sql",
+    "src/trace_processor/metrics/android/java_heap_histogram.sql",
     "src/trace_processor/metrics/android/java_heap_stats.sql",
     "src/trace_processor/metrics/android/mem_stats_priority_breakdown.sql",
     "src/trace_processor/metrics/android/process_mem.sql",
@@ -88,6 +93,8 @@
     ":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_track_event_cpp_gen",
+    ":perfetto_protos_perfetto_config_track_event_zero_gen",
     ":perfetto_protos_perfetto_config_zero_gen",
     ":perfetto_protos_perfetto_ipc_cpp_gen",
     ":perfetto_protos_perfetto_ipc_ipc_gen",
@@ -105,19 +112,26 @@
     ":perfetto_protos_perfetto_trace_profiling_zero_gen",
     ":perfetto_protos_perfetto_trace_ps_zero_gen",
     ":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
+    ":perfetto_protos_perfetto_trace_system_info_zero_gen",
     ":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_common_callstack_trie",
+    ":perfetto_src_profiling_common_interner",
+    ":perfetto_src_profiling_common_interning_output",
+    ":perfetto_src_profiling_common_proc_utils",
+    ":perfetto_src_profiling_common_unwind_support",
     ":perfetto_src_profiling_memory_daemon",
-    ":perfetto_src_profiling_memory_proc_utils",
     ":perfetto_src_profiling_memory_ring_buffer",
     ":perfetto_src_profiling_memory_scoped_spinlock",
     ":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: [
@@ -149,6 +163,8 @@
     "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_track_event_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_track_event_zero_gen_headers",
     "perfetto_protos_perfetto_config_zero_gen_headers",
     "perfetto_protos_perfetto_ipc_cpp_gen_headers",
     "perfetto_protos_perfetto_ipc_ipc_gen_headers",
@@ -166,6 +182,7 @@
     "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
     "perfetto_protos_perfetto_trace_ps_zero_gen_headers",
     "perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_system_info_zero_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
   ],
   defaults: [
@@ -186,9 +203,9 @@
     ":perfetto_include_perfetto_profiling_normalize",
     ":perfetto_src_base_base",
     ":perfetto_src_base_unix_socket",
+    ":perfetto_src_profiling_common_proc_utils",
     ":perfetto_src_profiling_memory_client",
     ":perfetto_src_profiling_memory_malloc_hooks",
-    ":perfetto_src_profiling_memory_proc_utils",
     ":perfetto_src_profiling_memory_ring_buffer",
     ":perfetto_src_profiling_memory_scoped_spinlock",
     ":perfetto_src_profiling_memory_wire_protocol",
@@ -275,6 +292,8 @@
     ":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_track_event_cpp_gen",
+    ":perfetto_protos_perfetto_config_track_event_zero_gen",
     ":perfetto_protos_perfetto_config_zero_gen",
     ":perfetto_protos_perfetto_ipc_cpp_gen",
     ":perfetto_protos_perfetto_ipc_ipc_gen",
@@ -292,19 +311,24 @@
     ":perfetto_protos_perfetto_trace_profiling_zero_gen",
     ":perfetto_protos_perfetto_trace_ps_zero_gen",
     ":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
+    ":perfetto_protos_perfetto_trace_system_info_zero_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_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",
+    ":perfetto_src_traced_probes_common_common",
     ":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_initial_display_state_initial_display_state",
     ":perfetto_src_traced_probes_metatrace_metatrace",
     ":perfetto_src_traced_probes_packages_list_packages_list",
     ":perfetto_src_traced_probes_power_power",
@@ -312,11 +336,16 @@
     ":perfetto_src_traced_probes_probes_src",
     ":perfetto_src_traced_probes_ps_ps",
     ":perfetto_src_traced_probes_sys_stats_sys_stats",
+    ":perfetto_src_traced_probes_system_info_system_info",
     ":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",
@@ -346,6 +375,8 @@
     "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_track_event_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_track_event_zero_gen_headers",
     "perfetto_protos_perfetto_config_zero_gen_headers",
     "perfetto_protos_perfetto_ipc_cpp_gen_headers",
     "perfetto_protos_perfetto_ipc_ipc_gen_headers",
@@ -363,6 +394,7 @@
     "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
     "perfetto_protos_perfetto_trace_ps_zero_gen_headers",
     "perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_system_info_zero_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
   ],
   defaults: [
@@ -446,6 +478,8 @@
     ":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_track_event_cpp_gen",
+    ":perfetto_protos_perfetto_config_track_event_zero_gen",
     ":perfetto_protos_perfetto_config_zero_gen",
     ":perfetto_protos_perfetto_ipc_cpp_gen",
     ":perfetto_protos_perfetto_ipc_ipc_gen",
@@ -463,16 +497,25 @@
     ":perfetto_protos_perfetto_trace_profiling_zero_gen",
     ":perfetto_protos_perfetto_trace_ps_zero_gen",
     ":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
+    ":perfetto_protos_perfetto_trace_system_info_zero_gen",
     ":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_without_backends",
     ":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_tracing",
+    ":perfetto_src_tracing_system_backend",
   ],
   export_include_dirs: [
     "include",
@@ -498,6 +541,8 @@
     "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_track_event_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_track_event_zero_gen_headers",
     "perfetto_protos_perfetto_config_zero_gen_headers",
     "perfetto_protos_perfetto_ipc_cpp_gen_headers",
     "perfetto_protos_perfetto_ipc_ipc_gen_headers",
@@ -515,6 +560,7 @@
     "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
     "perfetto_protos_perfetto_trace_ps_zero_gen_headers",
     "perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_system_info_zero_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
   ],
   export_generated_headers: [
@@ -537,6 +583,8 @@
     "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_track_event_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_track_event_zero_gen_headers",
     "perfetto_protos_perfetto_config_zero_gen_headers",
     "perfetto_protos_perfetto_ipc_cpp_gen_headers",
     "perfetto_protos_perfetto_ipc_ipc_gen_headers",
@@ -554,6 +602,7 @@
     "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
     "perfetto_protos_perfetto_trace_ps_zero_gen_headers",
     "perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_system_info_zero_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
   ],
   defaults: [
@@ -598,6 +647,8 @@
     ":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_track_event_cpp_gen",
+    ":perfetto_protos_perfetto_config_track_event_zero_gen",
     ":perfetto_protos_perfetto_config_zero_gen",
     ":perfetto_protos_perfetto_ipc_cpp_gen",
     ":perfetto_protos_perfetto_ipc_ipc_gen",
@@ -615,20 +666,24 @@
     ":perfetto_protos_perfetto_trace_profiling_zero_gen",
     ":perfetto_protos_perfetto_trace_ps_zero_gen",
     ":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
+    ":perfetto_protos_perfetto_trace_system_info_zero_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_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: [
@@ -655,6 +710,8 @@
     "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_track_event_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_track_event_zero_gen_headers",
     "perfetto_protos_perfetto_config_zero_gen_headers",
     "perfetto_protos_perfetto_ipc_cpp_gen_headers",
     "perfetto_protos_perfetto_ipc_ipc_gen_headers",
@@ -672,6 +729,7 @@
     "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
     "perfetto_protos_perfetto_trace_ps_zero_gen_headers",
     "perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_system_info_zero_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
     "perfetto_src_perfetto_cmd_protos_gen_headers",
   ],
@@ -719,6 +777,8 @@
     ":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_track_event_cpp_gen",
+    ":perfetto_protos_perfetto_config_track_event_zero_gen",
     ":perfetto_protos_perfetto_config_zero_gen",
     ":perfetto_protos_perfetto_ipc_cpp_gen",
     ":perfetto_protos_perfetto_ipc_ipc_gen",
@@ -749,6 +809,8 @@
     ":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_system_info_cpp_gen",
+    ":perfetto_protos_perfetto_trace_system_info_zero_gen",
     ":perfetto_protos_perfetto_trace_track_event_cpp_gen",
     ":perfetto_protos_perfetto_trace_track_event_zero_gen",
     ":perfetto_src_android_internal_headers",
@@ -756,29 +818,39 @@
     ":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",
+    ":perfetto_src_traced_probes_common_common",
     ":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_initial_display_state_initial_display_state",
     ":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_traced_probes_system_info_system_info",
     ":perfetto_src_tracing_common",
-    ":perfetto_src_tracing_ipc",
-    ":perfetto_src_tracing_tracing",
+    ":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",
     "test/cts/heapprofd_java_test_cts.cc",
     "test/cts/heapprofd_test_cts.cc",
+    "test/cts/traced_perf_test_cts.cc",
     "test/cts/utils.cc",
   ],
   static_libs: [
@@ -786,6 +858,9 @@
     "libgtest",
     "libperfetto_client_experimental",
   ],
+  whole_static_libs: [
+    "perfetto_gtest_logcat_printer",
+  ],
   export_include_dirs: [
     "include",
     "include/perfetto/base/build_configs/android_tree",
@@ -810,6 +885,8 @@
     "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_track_event_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_track_event_zero_gen_headers",
     "perfetto_protos_perfetto_config_zero_gen_headers",
     "perfetto_protos_perfetto_ipc_cpp_gen_headers",
     "perfetto_protos_perfetto_ipc_ipc_gen_headers",
@@ -840,6 +917,8 @@
     "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_system_info_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_system_info_zero_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_cpp_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
   ],
@@ -863,6 +942,8 @@
     "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_track_event_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_track_event_zero_gen_headers",
     "perfetto_protos_perfetto_config_zero_gen_headers",
     "perfetto_protos_perfetto_ipc_cpp_gen_headers",
     "perfetto_protos_perfetto_ipc_ipc_gen_headers",
@@ -893,6 +974,8 @@
     "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_system_info_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_system_info_zero_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_cpp_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
   ],
@@ -939,6 +1022,8 @@
     ":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_track_event_cpp_gen",
+    ":perfetto_protos_perfetto_config_track_event_zero_gen",
     ":perfetto_protos_perfetto_config_zero_gen",
     ":perfetto_protos_perfetto_ipc_cpp_gen",
     ":perfetto_protos_perfetto_ipc_ipc_gen",
@@ -969,6 +1054,8 @@
     ":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_system_info_cpp_gen",
+    ":perfetto_protos_perfetto_trace_system_info_zero_gen",
     ":perfetto_protos_perfetto_trace_track_event_cpp_gen",
     ":perfetto_protos_perfetto_trace_track_event_zero_gen",
     ":perfetto_src_android_internal_headers",
@@ -976,23 +1063,32 @@
     ":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",
+    ":perfetto_src_traced_probes_common_common",
     ":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_initial_display_state_initial_display_state",
     ":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_traced_probes_system_info_system_info",
     ":perfetto_src_tracing_common",
-    ":perfetto_src_tracing_ipc",
-    ":perfetto_src_tracing_tracing",
+    ":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: [
@@ -1019,6 +1115,8 @@
     "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_track_event_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_track_event_zero_gen_headers",
     "perfetto_protos_perfetto_config_zero_gen_headers",
     "perfetto_protos_perfetto_ipc_cpp_gen_headers",
     "perfetto_protos_perfetto_ipc_ipc_gen_headers",
@@ -1049,6 +1147,8 @@
     "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_system_info_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_system_info_zero_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_cpp_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
   ],
@@ -1072,6 +1172,8 @@
     "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_track_event_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_track_event_zero_gen_headers",
     "perfetto_protos_perfetto_config_zero_gen_headers",
     "perfetto_protos_perfetto_ipc_cpp_gen_headers",
     "perfetto_protos_perfetto_ipc_ipc_gen_headers",
@@ -1102,6 +1204,8 @@
     "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_system_info_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_system_info_zero_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_cpp_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
   ],
@@ -1144,6 +1248,25 @@
   },
 }
 
+// GN: //test:perfetto_gtest_logcat_printer
+cc_library_static {
+  name: "perfetto_gtest_logcat_printer",
+  srcs: [
+    "test/gtest_logcat_printer.cc",
+  ],
+  static_libs: [
+    "libgmock",
+    "libgtest",
+  ],
+  export_include_dirs: [
+    "include",
+    "include/perfetto/base/build_configs/android_tree",
+  ],
+  defaults: [
+    "perfetto_defaults",
+  ],
+}
+
 // GN: //include/perfetto/base:base
 filegroup {
   name: "perfetto_include_perfetto_base_base",
@@ -1159,6 +1282,11 @@
   name: "perfetto_include_perfetto_ext_ipc_ipc",
 }
 
+// GN: //include/perfetto/ext/trace_processor:export_json
+filegroup {
+  name: "perfetto_include_perfetto_ext_trace_processor_export_json",
+}
+
 // GN: //include/perfetto/ext/traced:sys_stats_counters
 filegroup {
   name: "perfetto_include_perfetto_ext_traced_sys_stats_counters",
@@ -1269,6 +1397,8 @@
     ":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_track_event_cpp_gen",
+    ":perfetto_protos_perfetto_config_track_event_zero_gen",
     ":perfetto_protos_perfetto_config_zero_gen",
     ":perfetto_protos_perfetto_ipc_cpp_gen",
     ":perfetto_protos_perfetto_ipc_ipc_gen",
@@ -1299,6 +1429,8 @@
     ":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_system_info_cpp_gen",
+    ":perfetto_protos_perfetto_trace_system_info_zero_gen",
     ":perfetto_protos_perfetto_trace_track_event_cpp_gen",
     ":perfetto_protos_perfetto_trace_track_event_zero_gen",
     ":perfetto_src_android_internal_headers",
@@ -1306,36 +1438,51 @@
     ":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_common_callstack_trie",
+    ":perfetto_src_profiling_common_interner",
+    ":perfetto_src_profiling_common_interning_output",
+    ":perfetto_src_profiling_common_proc_utils",
+    ":perfetto_src_profiling_common_unwind_support",
     ":perfetto_src_profiling_memory_client",
     ":perfetto_src_profiling_memory_daemon",
     ":perfetto_src_profiling_memory_end_to_end_tests",
-    ":perfetto_src_profiling_memory_proc_utils",
     ":perfetto_src_profiling_memory_ring_buffer",
     ":perfetto_src_profiling_memory_scoped_spinlock",
     ":perfetto_src_profiling_memory_wire_protocol",
     ":perfetto_src_protozero_protozero",
     ":perfetto_src_traced_probes_android_log_android_log",
+    ":perfetto_src_traced_probes_common_common",
     ":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_ftrace_integrationtests",
     ":perfetto_src_traced_probes_ftrace_test_support",
+    ":perfetto_src_traced_probes_initial_display_state_initial_display_state",
     ":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_client_api",
-    ":perfetto_src_tracing_client_api_integrationtests",
+    ":perfetto_src_traced_probes_system_info_system_info",
+    ":perfetto_src_tracing_client_api_without_backends",
     ":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_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_test_helper",
   ],
@@ -1350,6 +1497,9 @@
     "libgtest",
     "libperfetto_client_experimental",
   ],
+  whole_static_libs: [
+    "perfetto_gtest_logcat_printer",
+  ],
   generated_headers: [
     "perfetto_protos_perfetto_common_cpp_gen_headers",
     "perfetto_protos_perfetto_common_zero_gen_headers",
@@ -1370,6 +1520,8 @@
     "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_track_event_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_track_event_zero_gen_headers",
     "perfetto_protos_perfetto_config_zero_gen_headers",
     "perfetto_protos_perfetto_ipc_cpp_gen_headers",
     "perfetto_protos_perfetto_ipc_ipc_gen_headers",
@@ -1400,6 +1552,8 @@
     "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_system_info_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_system_info_zero_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_cpp_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
   ],
@@ -1424,6 +1578,7 @@
     "protos/perfetto/common/observable_events.proto",
     "protos/perfetto/common/sys_stats_counters.proto",
     "protos/perfetto/common/trace_stats.proto",
+    "protos/perfetto/common/tracing_service_capabilities.proto",
     "protos/perfetto/common/tracing_service_state.proto",
     "protos/perfetto/common/track_event_descriptor.proto",
   ],
@@ -1441,6 +1596,7 @@
     "external/perfetto/protos/perfetto/common/observable_events.gen.cc",
     "external/perfetto/protos/perfetto/common/sys_stats_counters.gen.cc",
     "external/perfetto/protos/perfetto/common/trace_stats.gen.cc",
+    "external/perfetto/protos/perfetto/common/tracing_service_capabilities.gen.cc",
     "external/perfetto/protos/perfetto/common/tracing_service_state.gen.cc",
     "external/perfetto/protos/perfetto/common/track_event_descriptor.gen.cc",
   ],
@@ -1458,6 +1614,7 @@
     "protos/perfetto/common/observable_events.proto",
     "protos/perfetto/common/sys_stats_counters.proto",
     "protos/perfetto/common/trace_stats.proto",
+    "protos/perfetto/common/tracing_service_capabilities.proto",
     "protos/perfetto/common/tracing_service_state.proto",
     "protos/perfetto/common/track_event_descriptor.proto",
   ],
@@ -1475,6 +1632,7 @@
     "external/perfetto/protos/perfetto/common/observable_events.gen.h",
     "external/perfetto/protos/perfetto/common/sys_stats_counters.gen.h",
     "external/perfetto/protos/perfetto/common/trace_stats.gen.h",
+    "external/perfetto/protos/perfetto/common/tracing_service_capabilities.gen.h",
     "external/perfetto/protos/perfetto/common/tracing_service_state.gen.h",
     "external/perfetto/protos/perfetto/common/track_event_descriptor.gen.h",
   ],
@@ -1496,6 +1654,7 @@
     "protos/perfetto/common/observable_events.proto",
     "protos/perfetto/common/sys_stats_counters.proto",
     "protos/perfetto/common/trace_stats.proto",
+    "protos/perfetto/common/tracing_service_capabilities.proto",
     "protos/perfetto/common/tracing_service_state.proto",
     "protos/perfetto/common/track_event_descriptor.proto",
   ],
@@ -1512,6 +1671,7 @@
     "external/perfetto/protos/perfetto/common/observable_events.pb.cc",
     "external/perfetto/protos/perfetto/common/sys_stats_counters.pb.cc",
     "external/perfetto/protos/perfetto/common/trace_stats.pb.cc",
+    "external/perfetto/protos/perfetto/common/tracing_service_capabilities.pb.cc",
     "external/perfetto/protos/perfetto/common/tracing_service_state.pb.cc",
     "external/perfetto/protos/perfetto/common/track_event_descriptor.pb.cc",
   ],
@@ -1529,6 +1689,7 @@
     "protos/perfetto/common/observable_events.proto",
     "protos/perfetto/common/sys_stats_counters.proto",
     "protos/perfetto/common/trace_stats.proto",
+    "protos/perfetto/common/tracing_service_capabilities.proto",
     "protos/perfetto/common/tracing_service_state.proto",
     "protos/perfetto/common/track_event_descriptor.proto",
   ],
@@ -1545,6 +1706,7 @@
     "external/perfetto/protos/perfetto/common/observable_events.pb.h",
     "external/perfetto/protos/perfetto/common/sys_stats_counters.pb.h",
     "external/perfetto/protos/perfetto/common/trace_stats.pb.h",
+    "external/perfetto/protos/perfetto/common/tracing_service_capabilities.pb.h",
     "external/perfetto/protos/perfetto/common/tracing_service_state.pb.h",
     "external/perfetto/protos/perfetto/common/track_event_descriptor.pb.h",
   ],
@@ -1566,6 +1728,7 @@
     "protos/perfetto/common/observable_events.proto",
     "protos/perfetto/common/sys_stats_counters.proto",
     "protos/perfetto/common/trace_stats.proto",
+    "protos/perfetto/common/tracing_service_capabilities.proto",
     "protos/perfetto/common/tracing_service_state.proto",
     "protos/perfetto/common/track_event_descriptor.proto",
   ],
@@ -1583,6 +1746,7 @@
     "external/perfetto/protos/perfetto/common/observable_events.pbzero.cc",
     "external/perfetto/protos/perfetto/common/sys_stats_counters.pbzero.cc",
     "external/perfetto/protos/perfetto/common/trace_stats.pbzero.cc",
+    "external/perfetto/protos/perfetto/common/tracing_service_capabilities.pbzero.cc",
     "external/perfetto/protos/perfetto/common/tracing_service_state.pbzero.cc",
     "external/perfetto/protos/perfetto/common/track_event_descriptor.pbzero.cc",
   ],
@@ -1600,6 +1764,7 @@
     "protos/perfetto/common/observable_events.proto",
     "protos/perfetto/common/sys_stats_counters.proto",
     "protos/perfetto/common/trace_stats.proto",
+    "protos/perfetto/common/tracing_service_capabilities.proto",
     "protos/perfetto/common/tracing_service_state.proto",
     "protos/perfetto/common/track_event_descriptor.proto",
   ],
@@ -1617,6 +1782,7 @@
     "external/perfetto/protos/perfetto/common/observable_events.pbzero.h",
     "external/perfetto/protos/perfetto/common/sys_stats_counters.pbzero.h",
     "external/perfetto/protos/perfetto/common/trace_stats.pbzero.h",
+    "external/perfetto/protos/perfetto/common/tracing_service_capabilities.pbzero.h",
     "external/perfetto/protos/perfetto/common/tracing_service_state.pbzero.h",
     "external/perfetto/protos/perfetto/common/track_event_descriptor.pbzero.h",
   ],
@@ -2616,6 +2782,112 @@
   ],
 }
 
+// GN: //protos/perfetto/config/track_event:cpp
+genrule {
+  name: "perfetto_protos_perfetto_config_track_event_cpp_gen",
+  srcs: [
+    "protos/perfetto/config/track_event/track_event_config.proto",
+  ],
+  tools: [
+    "aprotoc",
+    "perfetto_src_protozero_protoc_plugin_cppgen_plugin",
+  ],
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_cppgen_plugin) --plugin_out=wrapper_namespace=gen:$(genDir)/external/perfetto/ $(in)",
+  out: [
+    "external/perfetto/protos/perfetto/config/track_event/track_event_config.gen.cc",
+  ],
+}
+
+// GN: //protos/perfetto/config/track_event:cpp
+genrule {
+  name: "perfetto_protos_perfetto_config_track_event_cpp_gen_headers",
+  srcs: [
+    "protos/perfetto/config/track_event/track_event_config.proto",
+  ],
+  tools: [
+    "aprotoc",
+    "perfetto_src_protozero_protoc_plugin_cppgen_plugin",
+  ],
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_cppgen_plugin) --plugin_out=wrapper_namespace=gen:$(genDir)/external/perfetto/ $(in)",
+  out: [
+    "external/perfetto/protos/perfetto/config/track_event/track_event_config.gen.h",
+  ],
+  export_include_dirs: [
+    ".",
+    "protos",
+  ],
+}
+
+// GN: //protos/perfetto/config/track_event:lite
+genrule {
+  name: "perfetto_protos_perfetto_config_track_event_lite_gen",
+  srcs: [
+    "protos/perfetto/config/track_event/track_event_config.proto",
+  ],
+  tools: [
+    "aprotoc",
+  ],
+  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/track_event/track_event_config.pb.cc",
+  ],
+}
+
+// GN: //protos/perfetto/config/track_event:lite
+genrule {
+  name: "perfetto_protos_perfetto_config_track_event_lite_gen_headers",
+  srcs: [
+    "protos/perfetto/config/track_event/track_event_config.proto",
+  ],
+  tools: [
+    "aprotoc",
+  ],
+  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/track_event/track_event_config.pb.h",
+  ],
+  export_include_dirs: [
+    ".",
+    "protos",
+  ],
+}
+
+// GN: //protos/perfetto/config/track_event:zero
+genrule {
+  name: "perfetto_protos_perfetto_config_track_event_zero_gen",
+  srcs: [
+    "protos/perfetto/config/track_event/track_event_config.proto",
+  ],
+  tools: [
+    "aprotoc",
+    "protozero_plugin",
+  ],
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location protozero_plugin) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/ $(in)",
+  out: [
+    "external/perfetto/protos/perfetto/config/track_event/track_event_config.pbzero.cc",
+  ],
+}
+
+// GN: //protos/perfetto/config/track_event:zero
+genrule {
+  name: "perfetto_protos_perfetto_config_track_event_zero_gen_headers",
+  srcs: [
+    "protos/perfetto/config/track_event/track_event_config.proto",
+  ],
+  tools: [
+    "aprotoc",
+    "protozero_plugin",
+  ],
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location protozero_plugin) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/ $(in)",
+  out: [
+    "external/perfetto/protos/perfetto/config/track_event/track_event_config.pbzero.h",
+  ],
+  export_include_dirs: [
+    ".",
+    "protos",
+  ],
+}
+
 // GN: //protos/perfetto/config:zero
 genrule {
   name: "perfetto_protos_perfetto_config_zero_gen",
@@ -2786,9 +3058,11 @@
   srcs: [
     "protos/perfetto/metrics/android/batt_metric.proto",
     "protos/perfetto/metrics/android/cpu_metric.proto",
+    "protos/perfetto/metrics/android/display_metrics.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_histogram.proto",
     "protos/perfetto/metrics/android/java_heap_stats.proto",
     "protos/perfetto/metrics/android/lmk_metric.proto",
     "protos/perfetto/metrics/android/lmk_reason_metric.proto",
@@ -2798,6 +3072,8 @@
     "protos/perfetto/metrics/android/powrails_metric.proto",
     "protos/perfetto/metrics/android/process_metadata.proto",
     "protos/perfetto/metrics/android/startup_metric.proto",
+    "protos/perfetto/metrics/android/task_names.proto",
+    "protos/perfetto/metrics/android/thread_time_in_state_metric.proto",
     "protos/perfetto/metrics/android/unmapped_java_symbols.proto",
     "protos/perfetto/metrics/android/unsymbolized_frames.proto",
   ],
@@ -2809,9 +3085,11 @@
   out: [
     "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/display_metrics.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_histogram.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/java_heap_stats.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/lmk_metric.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/lmk_reason_metric.pbzero.cc",
@@ -2821,6 +3099,8 @@
     "external/perfetto/protos/perfetto/metrics/android/powrails_metric.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/process_metadata.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/startup_metric.pbzero.cc",
+    "external/perfetto/protos/perfetto/metrics/android/task_names.pbzero.cc",
+    "external/perfetto/protos/perfetto/metrics/android/thread_time_in_state_metric.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/unmapped_java_symbols.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/unsymbolized_frames.pbzero.cc",
   ],
@@ -2832,9 +3112,11 @@
   srcs: [
     "protos/perfetto/metrics/android/batt_metric.proto",
     "protos/perfetto/metrics/android/cpu_metric.proto",
+    "protos/perfetto/metrics/android/display_metrics.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_histogram.proto",
     "protos/perfetto/metrics/android/java_heap_stats.proto",
     "protos/perfetto/metrics/android/lmk_metric.proto",
     "protos/perfetto/metrics/android/lmk_reason_metric.proto",
@@ -2844,6 +3126,8 @@
     "protos/perfetto/metrics/android/powrails_metric.proto",
     "protos/perfetto/metrics/android/process_metadata.proto",
     "protos/perfetto/metrics/android/startup_metric.proto",
+    "protos/perfetto/metrics/android/task_names.proto",
+    "protos/perfetto/metrics/android/thread_time_in_state_metric.proto",
     "protos/perfetto/metrics/android/unmapped_java_symbols.proto",
     "protos/perfetto/metrics/android/unsymbolized_frames.proto",
   ],
@@ -2855,9 +3139,11 @@
   out: [
     "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/display_metrics.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_histogram.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/java_heap_stats.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/lmk_metric.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/lmk_reason_metric.pbzero.h",
@@ -2867,6 +3153,8 @@
     "external/perfetto/protos/perfetto/metrics/android/powrails_metric.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/process_metadata.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/startup_metric.pbzero.h",
+    "external/perfetto/protos/perfetto/metrics/android/task_names.pbzero.h",
+    "external/perfetto/protos/perfetto/metrics/android/thread_time_in_state_metric.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/unmapped_java_symbols.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/unsymbolized_frames.pbzero.h",
   ],
@@ -2918,6 +3206,7 @@
   srcs: [
     "protos/perfetto/trace/android/android_log.proto",
     "protos/perfetto/trace/android/graphics_frame_event.proto",
+    "protos/perfetto/trace/android/initial_display_state.proto",
     "protos/perfetto/trace/android/packages_list.proto",
   ],
   tools: [
@@ -2928,6 +3217,7 @@
   out: [
     "external/perfetto/protos/perfetto/trace/android/android_log.gen.cc",
     "external/perfetto/protos/perfetto/trace/android/graphics_frame_event.gen.cc",
+    "external/perfetto/protos/perfetto/trace/android/initial_display_state.gen.cc",
     "external/perfetto/protos/perfetto/trace/android/packages_list.gen.cc",
   ],
 }
@@ -2938,6 +3228,7 @@
   srcs: [
     "protos/perfetto/trace/android/android_log.proto",
     "protos/perfetto/trace/android/graphics_frame_event.proto",
+    "protos/perfetto/trace/android/initial_display_state.proto",
     "protos/perfetto/trace/android/packages_list.proto",
   ],
   tools: [
@@ -2948,6 +3239,7 @@
   out: [
     "external/perfetto/protos/perfetto/trace/android/android_log.gen.h",
     "external/perfetto/protos/perfetto/trace/android/graphics_frame_event.gen.h",
+    "external/perfetto/protos/perfetto/trace/android/initial_display_state.gen.h",
     "external/perfetto/protos/perfetto/trace/android/packages_list.gen.h",
   ],
   export_include_dirs: [
@@ -2962,6 +3254,7 @@
   srcs: [
     "protos/perfetto/trace/android/android_log.proto",
     "protos/perfetto/trace/android/graphics_frame_event.proto",
+    "protos/perfetto/trace/android/initial_display_state.proto",
     "protos/perfetto/trace/android/packages_list.proto",
   ],
   tools: [
@@ -2971,6 +3264,7 @@
   out: [
     "external/perfetto/protos/perfetto/trace/android/android_log.pb.cc",
     "external/perfetto/protos/perfetto/trace/android/graphics_frame_event.pb.cc",
+    "external/perfetto/protos/perfetto/trace/android/initial_display_state.pb.cc",
     "external/perfetto/protos/perfetto/trace/android/packages_list.pb.cc",
   ],
 }
@@ -2981,6 +3275,7 @@
   srcs: [
     "protos/perfetto/trace/android/android_log.proto",
     "protos/perfetto/trace/android/graphics_frame_event.proto",
+    "protos/perfetto/trace/android/initial_display_state.proto",
     "protos/perfetto/trace/android/packages_list.proto",
   ],
   tools: [
@@ -2990,6 +3285,7 @@
   out: [
     "external/perfetto/protos/perfetto/trace/android/android_log.pb.h",
     "external/perfetto/protos/perfetto/trace/android/graphics_frame_event.pb.h",
+    "external/perfetto/protos/perfetto/trace/android/initial_display_state.pb.h",
     "external/perfetto/protos/perfetto/trace/android/packages_list.pb.h",
   ],
   export_include_dirs: [
@@ -3004,6 +3300,7 @@
   srcs: [
     "protos/perfetto/trace/android/android_log.proto",
     "protos/perfetto/trace/android/graphics_frame_event.proto",
+    "protos/perfetto/trace/android/initial_display_state.proto",
     "protos/perfetto/trace/android/packages_list.proto",
   ],
   tools: [
@@ -3014,6 +3311,7 @@
   out: [
     "external/perfetto/protos/perfetto/trace/android/android_log.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/android/graphics_frame_event.pbzero.cc",
+    "external/perfetto/protos/perfetto/trace/android/initial_display_state.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/android/packages_list.pbzero.cc",
   ],
 }
@@ -3024,6 +3322,7 @@
   srcs: [
     "protos/perfetto/trace/android/android_log.proto",
     "protos/perfetto/trace/android/graphics_frame_event.proto",
+    "protos/perfetto/trace/android/initial_display_state.proto",
     "protos/perfetto/trace/android/packages_list.proto",
   ],
   tools: [
@@ -3034,6 +3333,7 @@
   out: [
     "external/perfetto/protos/perfetto/trace/android/android_log.pbzero.h",
     "external/perfetto/protos/perfetto/trace/android/graphics_frame_event.pbzero.h",
+    "external/perfetto/protos/perfetto/trace/android/initial_display_state.pbzero.h",
     "external/perfetto/protos/perfetto/trace/android/packages_list.pbzero.h",
   ],
   export_include_dirs: [
@@ -3297,6 +3597,7 @@
     "protos/perfetto/trace/ftrace/ftrace_stats.proto",
     "protos/perfetto/trace/ftrace/generic.proto",
     "protos/perfetto/trace/ftrace/i2c.proto",
+    "protos/perfetto/trace/ftrace/ion.proto",
     "protos/perfetto/trace/ftrace/ipi.proto",
     "protos/perfetto/trace/ftrace/irq.proto",
     "protos/perfetto/trace/ftrace/kmem.proto",
@@ -3338,6 +3639,7 @@
     "external/perfetto/protos/perfetto/trace/ftrace/ftrace_stats.gen.cc",
     "external/perfetto/protos/perfetto/trace/ftrace/generic.gen.cc",
     "external/perfetto/protos/perfetto/trace/ftrace/i2c.gen.cc",
+    "external/perfetto/protos/perfetto/trace/ftrace/ion.gen.cc",
     "external/perfetto/protos/perfetto/trace/ftrace/ipi.gen.cc",
     "external/perfetto/protos/perfetto/trace/ftrace/irq.gen.cc",
     "external/perfetto/protos/perfetto/trace/ftrace/kmem.gen.cc",
@@ -3379,6 +3681,7 @@
     "protos/perfetto/trace/ftrace/ftrace_stats.proto",
     "protos/perfetto/trace/ftrace/generic.proto",
     "protos/perfetto/trace/ftrace/i2c.proto",
+    "protos/perfetto/trace/ftrace/ion.proto",
     "protos/perfetto/trace/ftrace/ipi.proto",
     "protos/perfetto/trace/ftrace/irq.proto",
     "protos/perfetto/trace/ftrace/kmem.proto",
@@ -3420,6 +3723,7 @@
     "external/perfetto/protos/perfetto/trace/ftrace/ftrace_stats.gen.h",
     "external/perfetto/protos/perfetto/trace/ftrace/generic.gen.h",
     "external/perfetto/protos/perfetto/trace/ftrace/i2c.gen.h",
+    "external/perfetto/protos/perfetto/trace/ftrace/ion.gen.h",
     "external/perfetto/protos/perfetto/trace/ftrace/ipi.gen.h",
     "external/perfetto/protos/perfetto/trace/ftrace/irq.gen.h",
     "external/perfetto/protos/perfetto/trace/ftrace/kmem.gen.h",
@@ -3465,6 +3769,7 @@
     "protos/perfetto/trace/ftrace/ftrace_stats.proto",
     "protos/perfetto/trace/ftrace/generic.proto",
     "protos/perfetto/trace/ftrace/i2c.proto",
+    "protos/perfetto/trace/ftrace/ion.proto",
     "protos/perfetto/trace/ftrace/ipi.proto",
     "protos/perfetto/trace/ftrace/irq.proto",
     "protos/perfetto/trace/ftrace/kmem.proto",
@@ -3505,6 +3810,7 @@
     "external/perfetto/protos/perfetto/trace/ftrace/ftrace_stats.pb.cc",
     "external/perfetto/protos/perfetto/trace/ftrace/generic.pb.cc",
     "external/perfetto/protos/perfetto/trace/ftrace/i2c.pb.cc",
+    "external/perfetto/protos/perfetto/trace/ftrace/ion.pb.cc",
     "external/perfetto/protos/perfetto/trace/ftrace/ipi.pb.cc",
     "external/perfetto/protos/perfetto/trace/ftrace/irq.pb.cc",
     "external/perfetto/protos/perfetto/trace/ftrace/kmem.pb.cc",
@@ -3546,6 +3852,7 @@
     "protos/perfetto/trace/ftrace/ftrace_stats.proto",
     "protos/perfetto/trace/ftrace/generic.proto",
     "protos/perfetto/trace/ftrace/i2c.proto",
+    "protos/perfetto/trace/ftrace/ion.proto",
     "protos/perfetto/trace/ftrace/ipi.proto",
     "protos/perfetto/trace/ftrace/irq.proto",
     "protos/perfetto/trace/ftrace/kmem.proto",
@@ -3586,6 +3893,7 @@
     "external/perfetto/protos/perfetto/trace/ftrace/ftrace_stats.pb.h",
     "external/perfetto/protos/perfetto/trace/ftrace/generic.pb.h",
     "external/perfetto/protos/perfetto/trace/ftrace/i2c.pb.h",
+    "external/perfetto/protos/perfetto/trace/ftrace/ion.pb.h",
     "external/perfetto/protos/perfetto/trace/ftrace/ipi.pb.h",
     "external/perfetto/protos/perfetto/trace/ftrace/irq.pb.h",
     "external/perfetto/protos/perfetto/trace/ftrace/kmem.pb.h",
@@ -3631,6 +3939,7 @@
     "protos/perfetto/trace/ftrace/ftrace_stats.proto",
     "protos/perfetto/trace/ftrace/generic.proto",
     "protos/perfetto/trace/ftrace/i2c.proto",
+    "protos/perfetto/trace/ftrace/ion.proto",
     "protos/perfetto/trace/ftrace/ipi.proto",
     "protos/perfetto/trace/ftrace/irq.proto",
     "protos/perfetto/trace/ftrace/kmem.proto",
@@ -3672,6 +3981,7 @@
     "external/perfetto/protos/perfetto/trace/ftrace/ftrace_stats.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/ftrace/generic.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/ftrace/i2c.pbzero.cc",
+    "external/perfetto/protos/perfetto/trace/ftrace/ion.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/ftrace/ipi.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/ftrace/irq.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/ftrace/kmem.pbzero.cc",
@@ -3713,6 +4023,7 @@
     "protos/perfetto/trace/ftrace/ftrace_stats.proto",
     "protos/perfetto/trace/ftrace/generic.proto",
     "protos/perfetto/trace/ftrace/i2c.proto",
+    "protos/perfetto/trace/ftrace/ion.proto",
     "protos/perfetto/trace/ftrace/ipi.proto",
     "protos/perfetto/trace/ftrace/irq.proto",
     "protos/perfetto/trace/ftrace/kmem.proto",
@@ -3754,6 +4065,7 @@
     "external/perfetto/protos/perfetto/trace/ftrace/ftrace_stats.pbzero.h",
     "external/perfetto/protos/perfetto/trace/ftrace/generic.pbzero.h",
     "external/perfetto/protos/perfetto/trace/ftrace/i2c.pbzero.h",
+    "external/perfetto/protos/perfetto/trace/ftrace/ion.pbzero.h",
     "external/perfetto/protos/perfetto/trace/ftrace/ipi.pbzero.h",
     "external/perfetto/protos/perfetto/trace/ftrace/irq.pbzero.h",
     "external/perfetto/protos/perfetto/trace/ftrace/kmem.pbzero.h",
@@ -4317,6 +4629,7 @@
   name: "perfetto_protos_perfetto_trace_perfetto_cpp_gen",
   srcs: [
     "protos/perfetto/trace/perfetto/perfetto_metatrace.proto",
+    "protos/perfetto/trace/perfetto/tracing_service_event.proto",
   ],
   tools: [
     "aprotoc",
@@ -4325,6 +4638,7 @@
   cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_cppgen_plugin) --plugin_out=wrapper_namespace=gen:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/perfetto/perfetto_metatrace.gen.cc",
+    "external/perfetto/protos/perfetto/trace/perfetto/tracing_service_event.gen.cc",
   ],
 }
 
@@ -4333,6 +4647,7 @@
   name: "perfetto_protos_perfetto_trace_perfetto_cpp_gen_headers",
   srcs: [
     "protos/perfetto/trace/perfetto/perfetto_metatrace.proto",
+    "protos/perfetto/trace/perfetto/tracing_service_event.proto",
   ],
   tools: [
     "aprotoc",
@@ -4341,6 +4656,7 @@
   cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_cppgen_plugin) --plugin_out=wrapper_namespace=gen:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/perfetto/perfetto_metatrace.gen.h",
+    "external/perfetto/protos/perfetto/trace/perfetto/tracing_service_event.gen.h",
   ],
   export_include_dirs: [
     ".",
@@ -4353,6 +4669,7 @@
   name: "perfetto_protos_perfetto_trace_perfetto_lite_gen",
   srcs: [
     "protos/perfetto/trace/perfetto/perfetto_metatrace.proto",
+    "protos/perfetto/trace/perfetto/tracing_service_event.proto",
   ],
   tools: [
     "aprotoc",
@@ -4360,6 +4677,7 @@
   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",
+    "external/perfetto/protos/perfetto/trace/perfetto/tracing_service_event.pb.cc",
   ],
 }
 
@@ -4368,6 +4686,7 @@
   name: "perfetto_protos_perfetto_trace_perfetto_lite_gen_headers",
   srcs: [
     "protos/perfetto/trace/perfetto/perfetto_metatrace.proto",
+    "protos/perfetto/trace/perfetto/tracing_service_event.proto",
   ],
   tools: [
     "aprotoc",
@@ -4375,6 +4694,7 @@
   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",
+    "external/perfetto/protos/perfetto/trace/perfetto/tracing_service_event.pb.h",
   ],
   export_include_dirs: [
     ".",
@@ -4387,6 +4707,7 @@
   name: "perfetto_protos_perfetto_trace_perfetto_zero_gen",
   srcs: [
     "protos/perfetto/trace/perfetto/perfetto_metatrace.proto",
+    "protos/perfetto/trace/perfetto/tracing_service_event.proto",
   ],
   tools: [
     "aprotoc",
@@ -4395,6 +4716,7 @@
   cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location protozero_plugin) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/perfetto/perfetto_metatrace.pbzero.cc",
+    "external/perfetto/protos/perfetto/trace/perfetto/tracing_service_event.pbzero.cc",
   ],
 }
 
@@ -4403,6 +4725,7 @@
   name: "perfetto_protos_perfetto_trace_perfetto_zero_gen_headers",
   srcs: [
     "protos/perfetto/trace/perfetto/perfetto_metatrace.proto",
+    "protos/perfetto/trace/perfetto/tracing_service_event.proto",
   ],
   tools: [
     "aprotoc",
@@ -4411,6 +4734,7 @@
   cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location protozero_plugin) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/ $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/perfetto/perfetto_metatrace.pbzero.h",
+    "external/perfetto/protos/perfetto/trace/perfetto/tracing_service_event.pbzero.h",
   ],
   export_include_dirs: [
     ".",
@@ -4579,6 +4903,7 @@
     "protos/perfetto/trace/profiling/heap_graph.proto",
     "protos/perfetto/trace/profiling/profile_common.proto",
     "protos/perfetto/trace/profiling/profile_packet.proto",
+    "protos/perfetto/trace/profiling/smaps.proto",
   ],
   tools: [
     "aprotoc",
@@ -4589,6 +4914,7 @@
     "external/perfetto/protos/perfetto/trace/profiling/heap_graph.gen.cc",
     "external/perfetto/protos/perfetto/trace/profiling/profile_common.gen.cc",
     "external/perfetto/protos/perfetto/trace/profiling/profile_packet.gen.cc",
+    "external/perfetto/protos/perfetto/trace/profiling/smaps.gen.cc",
   ],
 }
 
@@ -4599,6 +4925,7 @@
     "protos/perfetto/trace/profiling/heap_graph.proto",
     "protos/perfetto/trace/profiling/profile_common.proto",
     "protos/perfetto/trace/profiling/profile_packet.proto",
+    "protos/perfetto/trace/profiling/smaps.proto",
   ],
   tools: [
     "aprotoc",
@@ -4609,6 +4936,7 @@
     "external/perfetto/protos/perfetto/trace/profiling/heap_graph.gen.h",
     "external/perfetto/protos/perfetto/trace/profiling/profile_common.gen.h",
     "external/perfetto/protos/perfetto/trace/profiling/profile_packet.gen.h",
+    "external/perfetto/protos/perfetto/trace/profiling/smaps.gen.h",
   ],
   export_include_dirs: [
     ".",
@@ -4623,6 +4951,7 @@
     "protos/perfetto/trace/profiling/heap_graph.proto",
     "protos/perfetto/trace/profiling/profile_common.proto",
     "protos/perfetto/trace/profiling/profile_packet.proto",
+    "protos/perfetto/trace/profiling/smaps.proto",
   ],
   tools: [
     "aprotoc",
@@ -4632,6 +4961,7 @@
     "external/perfetto/protos/perfetto/trace/profiling/heap_graph.pb.cc",
     "external/perfetto/protos/perfetto/trace/profiling/profile_common.pb.cc",
     "external/perfetto/protos/perfetto/trace/profiling/profile_packet.pb.cc",
+    "external/perfetto/protos/perfetto/trace/profiling/smaps.pb.cc",
   ],
 }
 
@@ -4642,6 +4972,7 @@
     "protos/perfetto/trace/profiling/heap_graph.proto",
     "protos/perfetto/trace/profiling/profile_common.proto",
     "protos/perfetto/trace/profiling/profile_packet.proto",
+    "protos/perfetto/trace/profiling/smaps.proto",
   ],
   tools: [
     "aprotoc",
@@ -4651,6 +4982,7 @@
     "external/perfetto/protos/perfetto/trace/profiling/heap_graph.pb.h",
     "external/perfetto/protos/perfetto/trace/profiling/profile_common.pb.h",
     "external/perfetto/protos/perfetto/trace/profiling/profile_packet.pb.h",
+    "external/perfetto/protos/perfetto/trace/profiling/smaps.pb.h",
   ],
   export_include_dirs: [
     ".",
@@ -4665,6 +4997,7 @@
     "protos/perfetto/trace/profiling/heap_graph.proto",
     "protos/perfetto/trace/profiling/profile_common.proto",
     "protos/perfetto/trace/profiling/profile_packet.proto",
+    "protos/perfetto/trace/profiling/smaps.proto",
   ],
   tools: [
     "aprotoc",
@@ -4675,6 +5008,7 @@
     "external/perfetto/protos/perfetto/trace/profiling/heap_graph.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/profiling/profile_common.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/profiling/profile_packet.pbzero.cc",
+    "external/perfetto/protos/perfetto/trace/profiling/smaps.pbzero.cc",
   ],
 }
 
@@ -4685,6 +5019,7 @@
     "protos/perfetto/trace/profiling/heap_graph.proto",
     "protos/perfetto/trace/profiling/profile_common.proto",
     "protos/perfetto/trace/profiling/profile_packet.proto",
+    "protos/perfetto/trace/profiling/smaps.proto",
   ],
   tools: [
     "aprotoc",
@@ -4695,6 +5030,7 @@
     "external/perfetto/protos/perfetto/trace/profiling/heap_graph.pbzero.h",
     "external/perfetto/protos/perfetto/trace/profiling/profile_common.pbzero.h",
     "external/perfetto/protos/perfetto/trace/profiling/profile_packet.pbzero.h",
+    "external/perfetto/protos/perfetto/trace/profiling/smaps.pbzero.h",
   ],
   export_include_dirs: [
     ".",
@@ -4926,6 +5262,112 @@
   ],
 }
 
+// GN: //protos/perfetto/trace/system_info:cpp
+genrule {
+  name: "perfetto_protos_perfetto_trace_system_info_cpp_gen",
+  srcs: [
+    "protos/perfetto/trace/system_info/cpu_info.proto",
+  ],
+  tools: [
+    "aprotoc",
+    "perfetto_src_protozero_protoc_plugin_cppgen_plugin",
+  ],
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_cppgen_plugin) --plugin_out=wrapper_namespace=gen:$(genDir)/external/perfetto/ $(in)",
+  out: [
+    "external/perfetto/protos/perfetto/trace/system_info/cpu_info.gen.cc",
+  ],
+}
+
+// GN: //protos/perfetto/trace/system_info:cpp
+genrule {
+  name: "perfetto_protos_perfetto_trace_system_info_cpp_gen_headers",
+  srcs: [
+    "protos/perfetto/trace/system_info/cpu_info.proto",
+  ],
+  tools: [
+    "aprotoc",
+    "perfetto_src_protozero_protoc_plugin_cppgen_plugin",
+  ],
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_cppgen_plugin) --plugin_out=wrapper_namespace=gen:$(genDir)/external/perfetto/ $(in)",
+  out: [
+    "external/perfetto/protos/perfetto/trace/system_info/cpu_info.gen.h",
+  ],
+  export_include_dirs: [
+    ".",
+    "protos",
+  ],
+}
+
+// GN: //protos/perfetto/trace/system_info:lite
+genrule {
+  name: "perfetto_protos_perfetto_trace_system_info_lite_gen",
+  srcs: [
+    "protos/perfetto/trace/system_info/cpu_info.proto",
+  ],
+  tools: [
+    "aprotoc",
+  ],
+  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/system_info/cpu_info.pb.cc",
+  ],
+}
+
+// GN: //protos/perfetto/trace/system_info:lite
+genrule {
+  name: "perfetto_protos_perfetto_trace_system_info_lite_gen_headers",
+  srcs: [
+    "protos/perfetto/trace/system_info/cpu_info.proto",
+  ],
+  tools: [
+    "aprotoc",
+  ],
+  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/system_info/cpu_info.pb.h",
+  ],
+  export_include_dirs: [
+    ".",
+    "protos",
+  ],
+}
+
+// GN: //protos/perfetto/trace/system_info:zero
+genrule {
+  name: "perfetto_protos_perfetto_trace_system_info_zero_gen",
+  srcs: [
+    "protos/perfetto/trace/system_info/cpu_info.proto",
+  ],
+  tools: [
+    "aprotoc",
+    "protozero_plugin",
+  ],
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location protozero_plugin) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/ $(in)",
+  out: [
+    "external/perfetto/protos/perfetto/trace/system_info/cpu_info.pbzero.cc",
+  ],
+}
+
+// GN: //protos/perfetto/trace/system_info:zero
+genrule {
+  name: "perfetto_protos_perfetto_trace_system_info_zero_gen_headers",
+  srcs: [
+    "protos/perfetto/trace/system_info/cpu_info.proto",
+  ],
+  tools: [
+    "aprotoc",
+    "protozero_plugin",
+  ],
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location protozero_plugin) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/ $(in)",
+  out: [
+    "external/perfetto/protos/perfetto/trace/system_info/cpu_info.pbzero.h",
+  ],
+  export_include_dirs: [
+    ".",
+    "protos",
+  ],
+}
+
 // GN: //protos/perfetto/trace/track_event:cpp
 genrule {
   name: "perfetto_protos_perfetto_trace_track_event_cpp_gen",
@@ -4933,10 +5375,12 @@
     "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto",
     "protos/perfetto/trace/track_event/chrome_histogram_sample.proto",
     "protos/perfetto/trace/track_event/chrome_keyed_service.proto",
+    "protos/perfetto/trace/track_event/chrome_latency_info.proto",
     "protos/perfetto/trace/track_event/chrome_legacy_ipc.proto",
     "protos/perfetto/trace/track_event/chrome_process_descriptor.proto",
     "protos/perfetto/trace/track_event/chrome_thread_descriptor.proto",
     "protos/perfetto/trace/track_event/chrome_user_event.proto",
+    "protos/perfetto/trace/track_event/counter_descriptor.proto",
     "protos/perfetto/trace/track_event/debug_annotation.proto",
     "protos/perfetto/trace/track_event/log_message.proto",
     "protos/perfetto/trace/track_event/process_descriptor.proto",
@@ -4955,10 +5399,12 @@
     "external/perfetto/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.gen.cc",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_histogram_sample.gen.cc",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_keyed_service.gen.cc",
+    "external/perfetto/protos/perfetto/trace/track_event/chrome_latency_info.gen.cc",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_legacy_ipc.gen.cc",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_process_descriptor.gen.cc",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_thread_descriptor.gen.cc",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_user_event.gen.cc",
+    "external/perfetto/protos/perfetto/trace/track_event/counter_descriptor.gen.cc",
     "external/perfetto/protos/perfetto/trace/track_event/debug_annotation.gen.cc",
     "external/perfetto/protos/perfetto/trace/track_event/log_message.gen.cc",
     "external/perfetto/protos/perfetto/trace/track_event/process_descriptor.gen.cc",
@@ -4977,10 +5423,12 @@
     "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto",
     "protos/perfetto/trace/track_event/chrome_histogram_sample.proto",
     "protos/perfetto/trace/track_event/chrome_keyed_service.proto",
+    "protos/perfetto/trace/track_event/chrome_latency_info.proto",
     "protos/perfetto/trace/track_event/chrome_legacy_ipc.proto",
     "protos/perfetto/trace/track_event/chrome_process_descriptor.proto",
     "protos/perfetto/trace/track_event/chrome_thread_descriptor.proto",
     "protos/perfetto/trace/track_event/chrome_user_event.proto",
+    "protos/perfetto/trace/track_event/counter_descriptor.proto",
     "protos/perfetto/trace/track_event/debug_annotation.proto",
     "protos/perfetto/trace/track_event/log_message.proto",
     "protos/perfetto/trace/track_event/process_descriptor.proto",
@@ -4999,10 +5447,12 @@
     "external/perfetto/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.gen.h",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_histogram_sample.gen.h",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_keyed_service.gen.h",
+    "external/perfetto/protos/perfetto/trace/track_event/chrome_latency_info.gen.h",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_legacy_ipc.gen.h",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_process_descriptor.gen.h",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_thread_descriptor.gen.h",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_user_event.gen.h",
+    "external/perfetto/protos/perfetto/trace/track_event/counter_descriptor.gen.h",
     "external/perfetto/protos/perfetto/trace/track_event/debug_annotation.gen.h",
     "external/perfetto/protos/perfetto/trace/track_event/log_message.gen.h",
     "external/perfetto/protos/perfetto/trace/track_event/process_descriptor.gen.h",
@@ -5025,10 +5475,12 @@
     "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto",
     "protos/perfetto/trace/track_event/chrome_histogram_sample.proto",
     "protos/perfetto/trace/track_event/chrome_keyed_service.proto",
+    "protos/perfetto/trace/track_event/chrome_latency_info.proto",
     "protos/perfetto/trace/track_event/chrome_legacy_ipc.proto",
     "protos/perfetto/trace/track_event/chrome_process_descriptor.proto",
     "protos/perfetto/trace/track_event/chrome_thread_descriptor.proto",
     "protos/perfetto/trace/track_event/chrome_user_event.proto",
+    "protos/perfetto/trace/track_event/counter_descriptor.proto",
     "protos/perfetto/trace/track_event/debug_annotation.proto",
     "protos/perfetto/trace/track_event/log_message.proto",
     "protos/perfetto/trace/track_event/process_descriptor.proto",
@@ -5046,10 +5498,12 @@
     "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",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_keyed_service.pb.cc",
+    "external/perfetto/protos/perfetto/trace/track_event/chrome_latency_info.pb.cc",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_legacy_ipc.pb.cc",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_process_descriptor.pb.cc",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_thread_descriptor.pb.cc",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_user_event.pb.cc",
+    "external/perfetto/protos/perfetto/trace/track_event/counter_descriptor.pb.cc",
     "external/perfetto/protos/perfetto/trace/track_event/debug_annotation.pb.cc",
     "external/perfetto/protos/perfetto/trace/track_event/log_message.pb.cc",
     "external/perfetto/protos/perfetto/trace/track_event/process_descriptor.pb.cc",
@@ -5068,10 +5522,12 @@
     "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto",
     "protos/perfetto/trace/track_event/chrome_histogram_sample.proto",
     "protos/perfetto/trace/track_event/chrome_keyed_service.proto",
+    "protos/perfetto/trace/track_event/chrome_latency_info.proto",
     "protos/perfetto/trace/track_event/chrome_legacy_ipc.proto",
     "protos/perfetto/trace/track_event/chrome_process_descriptor.proto",
     "protos/perfetto/trace/track_event/chrome_thread_descriptor.proto",
     "protos/perfetto/trace/track_event/chrome_user_event.proto",
+    "protos/perfetto/trace/track_event/counter_descriptor.proto",
     "protos/perfetto/trace/track_event/debug_annotation.proto",
     "protos/perfetto/trace/track_event/log_message.proto",
     "protos/perfetto/trace/track_event/process_descriptor.proto",
@@ -5089,10 +5545,12 @@
     "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",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_keyed_service.pb.h",
+    "external/perfetto/protos/perfetto/trace/track_event/chrome_latency_info.pb.h",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_legacy_ipc.pb.h",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_process_descriptor.pb.h",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_thread_descriptor.pb.h",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_user_event.pb.h",
+    "external/perfetto/protos/perfetto/trace/track_event/counter_descriptor.pb.h",
     "external/perfetto/protos/perfetto/trace/track_event/debug_annotation.pb.h",
     "external/perfetto/protos/perfetto/trace/track_event/log_message.pb.h",
     "external/perfetto/protos/perfetto/trace/track_event/process_descriptor.pb.h",
@@ -5115,10 +5573,12 @@
     "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto",
     "protos/perfetto/trace/track_event/chrome_histogram_sample.proto",
     "protos/perfetto/trace/track_event/chrome_keyed_service.proto",
+    "protos/perfetto/trace/track_event/chrome_latency_info.proto",
     "protos/perfetto/trace/track_event/chrome_legacy_ipc.proto",
     "protos/perfetto/trace/track_event/chrome_process_descriptor.proto",
     "protos/perfetto/trace/track_event/chrome_thread_descriptor.proto",
     "protos/perfetto/trace/track_event/chrome_user_event.proto",
+    "protos/perfetto/trace/track_event/counter_descriptor.proto",
     "protos/perfetto/trace/track_event/debug_annotation.proto",
     "protos/perfetto/trace/track_event/log_message.proto",
     "protos/perfetto/trace/track_event/process_descriptor.proto",
@@ -5137,10 +5597,12 @@
     "external/perfetto/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_histogram_sample.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_keyed_service.pbzero.cc",
+    "external/perfetto/protos/perfetto/trace/track_event/chrome_latency_info.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_legacy_ipc.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_process_descriptor.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_thread_descriptor.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_user_event.pbzero.cc",
+    "external/perfetto/protos/perfetto/trace/track_event/counter_descriptor.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/track_event/debug_annotation.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/track_event/log_message.pbzero.cc",
     "external/perfetto/protos/perfetto/trace/track_event/process_descriptor.pbzero.cc",
@@ -5159,10 +5621,12 @@
     "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto",
     "protos/perfetto/trace/track_event/chrome_histogram_sample.proto",
     "protos/perfetto/trace/track_event/chrome_keyed_service.proto",
+    "protos/perfetto/trace/track_event/chrome_latency_info.proto",
     "protos/perfetto/trace/track_event/chrome_legacy_ipc.proto",
     "protos/perfetto/trace/track_event/chrome_process_descriptor.proto",
     "protos/perfetto/trace/track_event/chrome_thread_descriptor.proto",
     "protos/perfetto/trace/track_event/chrome_user_event.proto",
+    "protos/perfetto/trace/track_event/counter_descriptor.proto",
     "protos/perfetto/trace/track_event/debug_annotation.proto",
     "protos/perfetto/trace/track_event/log_message.proto",
     "protos/perfetto/trace/track_event/process_descriptor.proto",
@@ -5181,10 +5645,12 @@
     "external/perfetto/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.pbzero.h",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_histogram_sample.pbzero.h",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_keyed_service.pbzero.h",
+    "external/perfetto/protos/perfetto/trace/track_event/chrome_latency_info.pbzero.h",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_legacy_ipc.pbzero.h",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_process_descriptor.pbzero.h",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_thread_descriptor.pbzero.h",
     "external/perfetto/protos/perfetto/trace/track_event/chrome_user_event.pbzero.h",
+    "external/perfetto/protos/perfetto/trace/track_event/counter_descriptor.pbzero.h",
     "external/perfetto/protos/perfetto/trace/track_event/debug_annotation.pbzero.h",
     "external/perfetto/protos/perfetto/trace/track_event/log_message.pbzero.h",
     "external/perfetto/protos/perfetto/trace/track_event/process_descriptor.pbzero.h",
@@ -5275,6 +5741,7 @@
     "src/base/string_splitter.cc",
     "src/base/string_utils.cc",
     "src/base/string_view.cc",
+    "src/base/subprocess.cc",
     "src/base/temp_file.cc",
     "src/base/thread_checker.cc",
     "src/base/thread_task_runner.cc",
@@ -5312,6 +5779,7 @@
     "src/base/string_utils_unittest.cc",
     "src/base/string_view_unittest.cc",
     "src/base/string_writer_unittest.cc",
+    "src/base/subprocess_unittest.cc",
     "src/base/task_runner_unittest.cc",
     "src/base/temp_file_unittest.cc",
     "src/base/thread_checker_unittest.cc",
@@ -5333,19 +5801,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",
@@ -5527,6 +6009,52 @@
   ],
 }
 
+// GN: //src/profiling/common:callstack_trie
+filegroup {
+  name: "perfetto_src_profiling_common_callstack_trie",
+  srcs: [
+    "src/profiling/common/callstack_trie.cc",
+  ],
+}
+
+// GN: //src/profiling/common:interner
+filegroup {
+  name: "perfetto_src_profiling_common_interner",
+}
+
+// GN: //src/profiling/common:interning_output
+filegroup {
+  name: "perfetto_src_profiling_common_interning_output",
+  srcs: [
+    "src/profiling/common/interning_output.cc",
+  ],
+}
+
+// GN: //src/profiling/common:proc_utils
+filegroup {
+  name: "perfetto_src_profiling_common_proc_utils",
+  srcs: [
+    "src/profiling/common/proc_utils.cc",
+  ],
+}
+
+// GN: //src/profiling/common:unittests
+filegroup {
+  name: "perfetto_src_profiling_common_unittests",
+  srcs: [
+    "src/profiling/common/interner_unittest.cc",
+    "src/profiling/common/proc_utils_unittest.cc",
+  ],
+}
+
+// GN: //src/profiling/common:unwind_support
+filegroup {
+  name: "perfetto_src_profiling_common_unwind_support",
+  srcs: [
+    "src/profiling/common/unwind_support.cc",
+  ],
+}
+
 // GN: //src/profiling:deobfuscator
 filegroup {
   name: "perfetto_src_profiling_deobfuscator",
@@ -5554,7 +6082,6 @@
     "src/profiling/memory/page_idle_checker.cc",
     "src/profiling/memory/system_property.cc",
     "src/profiling/memory/unwinding.cc",
-    "src/profiling/memory/utils.cc",
   ],
 }
 
@@ -5574,14 +6101,6 @@
   ],
 }
 
-// GN: //src/profiling/memory:proc_utils
-filegroup {
-  name: "perfetto_src_profiling_memory_proc_utils",
-  srcs: [
-    "src/profiling/memory/proc_utils.cc",
-  ],
-}
-
 // GN: //src/profiling/memory:ring_buffer
 filegroup {
   name: "perfetto_src_profiling_memory_ring_buffer",
@@ -5613,9 +6132,8 @@
     "src/profiling/memory/bookkeeping_unittest.cc",
     "src/profiling/memory/client_unittest.cc",
     "src/profiling/memory/heapprofd_producer_unittest.cc",
-    "src/profiling/memory/interner_unittest.cc",
     "src/profiling/memory/page_idle_checker_unittest.cc",
-    "src/profiling/memory/proc_utils_unittest.cc",
+    "src/profiling/memory/parse_smaps_unittest.cc",
     "src/profiling/memory/sampler_unittest.cc",
     "src/profiling/memory/system_property_unittest.cc",
     "src/profiling/memory/unwinding_unittest.cc",
@@ -5631,10 +6149,24 @@
   ],
 }
 
+// GN: //src/profiling/perf:common_types
+filegroup {
+  name: "perfetto_src_profiling_perf_common_types",
+}
+
+// GN: //src/profiling/perf:proc_descriptors
+filegroup {
+  name: "perfetto_src_profiling_perf_proc_descriptors",
+  srcs: [
+    "src/profiling/perf/proc_descriptors.cc",
+  ],
+}
+
 // GN: //src/profiling/perf:producer
 filegroup {
   name: "perfetto_src_profiling_perf_producer",
   srcs: [
+    "src/profiling/perf/event_config.cc",
     "src/profiling/perf/event_reader.cc",
     "src/profiling/perf/perf_producer.cc",
   ],
@@ -5645,6 +6177,15 @@
   name: "perfetto_src_profiling_perf_producer_unittests",
   srcs: [
     "src/profiling/perf/event_config_unittest.cc",
+    "src/profiling/perf/unwind_queue_unittest.cc",
+  ],
+}
+
+// GN: //src/profiling/perf:regs_parsing
+filegroup {
+  name: "perfetto_src_profiling_perf_regs_parsing",
+  srcs: [
+    "src/profiling/perf/regs_parsing.cc",
   ],
 }
 
@@ -5656,11 +6197,11 @@
   ],
 }
 
-// GN: //src/profiling/perf:unwind_support
+// GN: //src/profiling/perf:unwinding
 filegroup {
-  name: "perfetto_src_profiling_perf_unwind_support",
+  name: "perfetto_src_profiling_perf_unwinding",
   srcs: [
-    "src/profiling/perf/unwind_support.cc",
+    "src/profiling/perf/unwinding.cc",
   ],
 }
 
@@ -5885,6 +6426,14 @@
   ],
 }
 
+// GN: //src/trace_processor/analysis:analysis
+filegroup {
+  name: "perfetto_src_trace_processor_analysis_analysis",
+  srcs: [
+    "src/trace_processor/analysis/describe_slice.cc",
+  ],
+}
+
 // GN: //src/trace_processor/containers:containers
 filegroup {
   name: "perfetto_src_trace_processor_containers_containers",
@@ -5892,6 +6441,7 @@
     "src/trace_processor/containers/bit_vector.cc",
     "src/trace_processor/containers/bit_vector_iterators.cc",
     "src/trace_processor/containers/row_map.cc",
+    "src/trace_processor/containers/sparse_vector.cc",
     "src/trace_processor/containers/string_pool.cc",
   ],
 }
@@ -5922,14 +6472,48 @@
   name: "perfetto_src_trace_processor_db_unittests",
   srcs: [
     "src/trace_processor/db/compare_unittest.cc",
+    "src/trace_processor/db/table_unittest.cc",
   ],
 }
 
-// GN: //src/trace_processor:descriptors
+// GN: //src/trace_processor:export_json
 filegroup {
-  name: "perfetto_src_trace_processor_descriptors",
+  name: "perfetto_src_trace_processor_export_json",
   srcs: [
-    "src/trace_processor/descriptors.cc",
+    "src/trace_processor/export_json.cc",
+  ],
+}
+
+// GN: //src/trace_processor:ftrace_descriptors
+filegroup {
+  name: "perfetto_src_trace_processor_ftrace_descriptors",
+  srcs: [
+    "src/trace_processor/importers/ftrace/ftrace_descriptors.cc",
+  ],
+}
+
+// GN: //src/trace_processor/importers:common
+filegroup {
+  name: "perfetto_src_trace_processor_importers_common",
+  srcs: [
+    "src/trace_processor/importers/common/args_tracker.cc",
+    "src/trace_processor/importers/common/clock_tracker.cc",
+    "src/trace_processor/importers/common/event_tracker.cc",
+    "src/trace_processor/importers/common/global_args_tracker.cc",
+    "src/trace_processor/importers/common/process_tracker.cc",
+    "src/trace_processor/importers/common/slice_tracker.cc",
+    "src/trace_processor/importers/common/track_tracker.cc",
+  ],
+}
+
+// GN: //src/trace_processor/importers:unittests
+filegroup {
+  name: "perfetto_src_trace_processor_importers_unittests",
+  srcs: [
+    "src/trace_processor/importers/common/clock_tracker_unittest.cc",
+    "src/trace_processor/importers/common/event_tracker_unittest.cc",
+    "src/trace_processor/importers/common/process_tracker_unittest.cc",
+    "src/trace_processor/importers/common/slice_tracker_unittest.cc",
   ],
 }
 
@@ -5937,21 +6521,13 @@
 filegroup {
   name: "perfetto_src_trace_processor_lib",
   srcs: [
-    "src/trace_processor/filtered_row_index.cc",
+    "src/trace_processor/dynamic/describe_slice_generator.cc",
+    "src/trace_processor/dynamic/experimental_counter_dur_generator.cc",
+    "src/trace_processor/dynamic/experimental_flamegraph_generator.cc",
+    "src/trace_processor/dynamic/experimental_slice_layout_generator.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/trace_processor.cc",
     "src/trace_processor/trace_processor_impl.cc",
-    "src/trace_processor/window_operator_table.cc",
   ],
 }
 
@@ -5971,22 +6547,19 @@
   ],
 }
 
-// GN: //src/trace_processor:protozero_to_text
-filegroup {
-  name: "perfetto_src_trace_processor_protozero_to_text",
-  srcs: [
-    "src/trace_processor/protozero_to_text.cc",
-  ],
-}
-
 // GN: //src/trace_processor/sqlite:sqlite
 filegroup {
   name: "perfetto_src_trace_processor_sqlite_sqlite",
   srcs: [
     "src/trace_processor/sqlite/db_sqlite_table.cc",
     "src/trace_processor/sqlite/query_constraints.cc",
+    "src/trace_processor/sqlite/span_join_operator_table.cc",
+    "src/trace_processor/sqlite/sql_stats_table.cc",
     "src/trace_processor/sqlite/sqlite3_str_split.cc",
+    "src/trace_processor/sqlite/sqlite_raw_table.cc",
     "src/trace_processor/sqlite/sqlite_table.cc",
+    "src/trace_processor/sqlite/stats_table.cc",
+    "src/trace_processor/sqlite/window_operator_table.cc",
   ],
 }
 
@@ -5996,6 +6569,7 @@
   srcs: [
     "src/trace_processor/sqlite/db_sqlite_table_unittest.cc",
     "src/trace_processor/sqlite/query_constraints_unittest.cc",
+    "src/trace_processor/sqlite/span_join_operator_table_unittest.cc",
     "src/trace_processor/sqlite/sqlite3_str_split_unittest.cc",
   ],
 }
@@ -6004,15 +6578,24 @@
 filegroup {
   name: "perfetto_src_trace_processor_storage_full",
   srcs: [
+    "src/trace_processor/importers/additional_modules.cc",
     "src/trace_processor/importers/ftrace/binder_tracker.cc",
-    "src/trace_processor/importers/ftrace/ftrace_descriptors.cc",
     "src/trace_processor/importers/ftrace/ftrace_module_impl.cc",
     "src/trace_processor/importers/ftrace/ftrace_parser.cc",
     "src/trace_processor/importers/ftrace/ftrace_tokenizer.cc",
     "src/trace_processor/importers/ftrace/rss_stat_tracker.cc",
     "src/trace_processor/importers/ftrace/sched_event_tracker.cc",
+    "src/trace_processor/importers/fuchsia/fuchsia_record.cc",
+    "src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc",
+    "src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc",
+    "src/trace_processor/importers/fuchsia/fuchsia_trace_utils.cc",
+    "src/trace_processor/importers/gzip/gzip_trace_parser.cc",
+    "src/trace_processor/importers/json/json_trace_parser.cc",
+    "src/trace_processor/importers/json/json_trace_tokenizer.cc",
+    "src/trace_processor/importers/json/json_tracker.cc",
     "src/trace_processor/importers/proto/android_probes_module.cc",
     "src/trace_processor/importers/proto/android_probes_parser.cc",
+    "src/trace_processor/importers/proto/android_probes_tracker.cc",
     "src/trace_processor/importers/proto/graphics_event_module.cc",
     "src/trace_processor/importers/proto/graphics_event_parser.cc",
     "src/trace_processor/importers/proto/heap_graph_module.cc",
@@ -6021,10 +6604,11 @@
     "src/trace_processor/importers/proto/system_probes_module.cc",
     "src/trace_processor/importers/proto/system_probes_parser.cc",
     "src/trace_processor/importers/proto/vulkan_memory_tracker.cc",
+    "src/trace_processor/importers/syscalls/syscall_tracker.cc",
+    "src/trace_processor/importers/systrace/systrace_line_parser.cc",
+    "src/trace_processor/importers/systrace/systrace_line_tokenizer.cc",
     "src/trace_processor/importers/systrace/systrace_parser.cc",
     "src/trace_processor/importers/systrace/systrace_trace_parser.cc",
-    "src/trace_processor/register_additional_modules.cc",
-    "src/trace_processor/syscall_tracker.cc",
   ],
 }
 
@@ -6032,45 +6616,48 @@
 filegroup {
   name: "perfetto_src_trace_processor_storage_minimal",
   srcs: [
-    "src/trace_processor/args_tracker.cc",
-    "src/trace_processor/clock_tracker.cc",
-    "src/trace_processor/destructible.cc",
-    "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/default_modules.cc",
     "src/trace_processor/importers/ftrace/ftrace_module.cc",
-    "src/trace_processor/importers/fuchsia/fuchsia_record.cc",
-    "src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc",
-    "src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc",
-    "src/trace_processor/importers/fuchsia/fuchsia_trace_utils.cc",
+    "src/trace_processor/importers/gzip/gzip_utils.cc",
+    "src/trace_processor/importers/json/json_utils.cc",
+    "src/trace_processor/importers/ninja/ninja_log_parser.cc",
     "src/trace_processor/importers/proto/args_table_utils.cc",
+    "src/trace_processor/importers/proto/heap_profile_tracker.cc",
+    "src/trace_processor/importers/proto/metadata_tracker.cc",
     "src/trace_processor/importers/proto/packet_sequence_state.cc",
+    "src/trace_processor/importers/proto/perf_sample_tracker.cc",
+    "src/trace_processor/importers/proto/profile_module.cc",
+    "src/trace_processor/importers/proto/profile_packet_utils.cc",
     "src/trace_processor/importers/proto/proto_importer_module.cc",
     "src/trace_processor/importers/proto/proto_trace_parser.cc",
     "src/trace_processor/importers/proto/proto_trace_tokenizer.cc",
+    "src/trace_processor/importers/proto/stack_profile_tracker.cc",
     "src/trace_processor/importers/proto/track_event_module.cc",
     "src/trace_processor/importers/proto/track_event_parser.cc",
     "src/trace_processor/importers/proto/track_event_tokenizer.cc",
-    "src/trace_processor/metadata_tracker.cc",
-    "src/trace_processor/process_tracker.cc",
-    "src/trace_processor/slice_tracker.cc",
-    "src/trace_processor/stack_profile_tracker.cc",
     "src/trace_processor/trace_processor_context.cc",
     "src/trace_processor/trace_processor_storage.cc",
     "src/trace_processor/trace_processor_storage_impl.cc",
     "src/trace_processor/trace_sorter.cc",
-    "src/trace_processor/trace_storage.cc",
-    "src/trace_processor/track_tracker.cc",
     "src/trace_processor/virtual_destructors.cc",
   ],
 }
 
+// GN: //src/trace_processor/storage:storage
+filegroup {
+  name: "perfetto_src_trace_processor_storage_storage",
+  srcs: [
+    "src/trace_processor/storage/trace_storage.cc",
+  ],
+}
+
 // GN: //src/trace_processor/tables:tables
 filegroup {
   name: "perfetto_src_trace_processor_tables_tables",
+  srcs: [
+    "src/trace_processor/tables/table_destructors.cc",
+  ],
 }
 
 // GN: //src/trace_processor/tables:unittests
@@ -6081,41 +6668,79 @@
   ],
 }
 
+// GN: //src/trace_processor:track_event_descriptor
+filegroup {
+  name: "perfetto_src_trace_processor_track_event_descriptor",
+}
+
 // GN: //src/trace_processor/types:types
 filegroup {
   name: "perfetto_src_trace_processor_types_types",
   srcs: [
+    "src/trace_processor/types/destructible.cc",
     "src/trace_processor/types/gfp_flags.cc",
+    "src/trace_processor/types/task_state.cc",
     "src/trace_processor/types/variadic.cc",
   ],
 }
 
+// GN: //src/trace_processor/types:unittests
+filegroup {
+  name: "perfetto_src_trace_processor_types_unittests",
+  srcs: [
+    "src/trace_processor/types/task_state_unittests.cc",
+  ],
+}
+
 // GN: //src/trace_processor:unittests
 filegroup {
   name: "perfetto_src_trace_processor_unittests",
   srcs: [
-    "src/trace_processor/clock_tracker_unittest.cc",
-    "src/trace_processor/event_tracker_unittest.cc",
-    "src/trace_processor/filtered_row_index_unittest.cc",
+    "src/trace_processor/dynamic/experimental_counter_dur_generator_unittest.cc",
+    "src/trace_processor/dynamic/experimental_slice_layout_generator_unittest.cc",
     "src/trace_processor/forwarding_trace_parser_unittest.cc",
-    "src/trace_processor/ftrace_utils_unittest.cc",
-    "src/trace_processor/heap_profile_tracker_unittest.cc",
+    "src/trace_processor/importers/ftrace/sched_event_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/heap_profile_tracker_unittest.cc",
     "src/trace_processor/importers/proto/proto_trace_parser_unittest.cc",
+    "src/trace_processor/importers/syscalls/syscall_tracker_unittest.cc",
     "src/trace_processor/importers/systrace/systrace_parser_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/trace_sorter_unittest.cc",
   ],
 }
 
+// GN: //src/trace_processor/util:descriptors
+filegroup {
+  name: "perfetto_src_trace_processor_util_descriptors",
+  srcs: [
+    "src/trace_processor/util/descriptors.cc",
+  ],
+}
+
+// GN: //src/trace_processor/util:protozero_to_text
+filegroup {
+  name: "perfetto_src_trace_processor_util_protozero_to_text",
+  srcs: [
+    "src/trace_processor/util/protozero_to_text.cc",
+  ],
+}
+
+// GN: //src/trace_processor/util:unittests
+filegroup {
+  name: "perfetto_src_trace_processor_util_unittests",
+  srcs: [
+    "src/trace_processor/util/protozero_to_text_unittests.cc",
+  ],
+}
+
+// GN: //src/trace_processor/util:util
+filegroup {
+  name: "perfetto_src_trace_processor_util_util",
+}
+
 // GN: //src/traced/probes/android_log:android_log
 filegroup {
   name: "perfetto_src_traced_probes_android_log_android_log",
@@ -6132,6 +6757,30 @@
   ],
 }
 
+// GN: //src/traced/probes/common:common
+filegroup {
+  name: "perfetto_src_traced_probes_common_common",
+  srcs: [
+    "src/traced/probes/common/cpu_freq_info.cc",
+  ],
+}
+
+// GN: //src/traced/probes/common:test_support
+filegroup {
+  name: "perfetto_src_traced_probes_common_test_support",
+  srcs: [
+    "src/traced/probes/common/cpu_freq_info_for_testing.cc",
+  ],
+}
+
+// GN: //src/traced/probes/common:unittests
+filegroup {
+  name: "perfetto_src_traced_probes_common_unittests",
+  srcs: [
+    "src/traced/probes/common/cpu_freq_info_unittest.cc",
+  ],
+}
+
 // GN: //src/traced/probes:data_source
 filegroup {
   name: "perfetto_src_traced_probes_data_source",
@@ -6183,6 +6832,7 @@
     "src/traced/probes/ftrace/compact_sched.cc",
     "src/traced/probes/ftrace/cpu_reader.cc",
     "src/traced/probes/ftrace/cpu_stats_parser.cc",
+    "src/traced/probes/ftrace/discover_vendor_tracepoints.cc",
     "src/traced/probes/ftrace/event_info.cc",
     "src/traced/probes/ftrace/event_info_constants.cc",
     "src/traced/probes/ftrace/ftrace_config_muxer.cc",
@@ -6339,6 +6989,7 @@
   srcs: [
     "src/traced/probes/ftrace/cpu_reader_unittest.cc",
     "src/traced/probes/ftrace/cpu_stats_parser_unittest.cc",
+    "src/traced/probes/ftrace/discover_vendor_tracepoints_unittest.cc",
     "src/traced/probes/ftrace/event_info_unittest.cc",
     "src/traced/probes/ftrace/format_parser_unittest.cc",
     "src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc",
@@ -6349,6 +7000,22 @@
   ],
 }
 
+// GN: //src/traced/probes/initial_display_state:initial_display_state
+filegroup {
+  name: "perfetto_src_traced_probes_initial_display_state_initial_display_state",
+  srcs: [
+    "src/traced/probes/initial_display_state/initial_display_state_data_source.cc",
+  ],
+}
+
+// GN: //src/traced/probes/initial_display_state:unittests
+filegroup {
+  name: "perfetto_src_traced_probes_initial_display_state_unittests",
+  srcs: [
+    "src/traced/probes/initial_display_state/initial_display_state_data_source_unittest.cc",
+  ],
+}
+
 // GN: //src/traced/probes/metatrace:metatrace
 filegroup {
   name: "perfetto_src_traced_probes_metatrace_metatrace",
@@ -6429,6 +7096,22 @@
   ],
 }
 
+// GN: //src/traced/probes/system_info:system_info
+filegroup {
+  name: "perfetto_src_traced_probes_system_info_system_info",
+  srcs: [
+    "src/traced/probes/system_info/system_info_data_source.cc",
+  ],
+}
+
+// GN: //src/traced/probes/system_info:unittests
+filegroup {
+  name: "perfetto_src_traced_probes_system_info_unittests",
+  srcs: [
+    "src/traced/probes/system_info/system_info_data_source_unittest.cc",
+  ],
+}
+
 // GN: //src/traced/probes:unittests
 filegroup {
   name: "perfetto_src_traced_probes_unittests",
@@ -6451,35 +7134,24 @@
   ],
 }
 
-// GN: //src/tracing:client_api
+// GN: //src/tracing:client_api_without_backends
 filegroup {
-  name: "perfetto_src_tracing_client_api",
+  name: "perfetto_src_tracing_client_api_without_backends",
   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/track_event_legacy.cc",
     "src/tracing/virtual_destructors.cc",
   ],
 }
 
-// GN: //src/tracing:client_api_integrationtests
-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",
-  ],
-}
-
 // GN: //src/tracing:common
 filegroup {
   name: "perfetto_src_tracing_common",
@@ -6488,28 +7160,116 @@
   ],
 }
 
-// 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/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/trace_packet.cc",
+    "src/tracing/core/trace_writer_impl.cc",
+    "src/tracing/core/virtual_destructors.cc",
+  ],
+}
+
+// GN: //src/tracing/core:service
+filegroup {
+  name: "perfetto_src_tracing_core_service",
+  srcs: [
+    "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/core:test_support
+filegroup {
+  name: "perfetto_src_tracing_core_test_support",
+  srcs: [
+    "src/tracing/core/trace_writer_for_testing.cc",
+  ],
+}
+
+// GN: //src/tracing/core:unittests
+filegroup {
+  name: "perfetto_src_tracing_core_unittests",
+  srcs: [
+    "src/tracing/core/id_allocator_unittest.cc",
+    "src/tracing/core/null_trace_writer_unittest.cc",
+    "src/tracing/core/packet_stream_validator_unittest.cc",
+    "src/tracing/core/patch_list_unittest.cc",
+    "src/tracing/core/shared_memory_abi_unittest.cc",
+    "src/tracing/core/shared_memory_arbiter_impl_unittest.cc",
+    "src/tracing/core/trace_buffer_unittest.cc",
+    "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/ipc:common
+filegroup {
+  name: "perfetto_src_tracing_ipc_common",
+  srcs: [
+    "src/tracing/ipc/default_socket.cc",
+    "src/tracing/ipc/memfd.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",
-    "src/tracing/ipc/default_socket.cc",
-    "src/tracing/ipc/posix_shared_memory.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",
@@ -6518,6 +7278,14 @@
   ],
 }
 
+// GN: //src/tracing:system_backend
+filegroup {
+  name: "perfetto_src_tracing_system_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",
@@ -6526,55 +7294,32 @@
   ],
 }
 
-// GN: //src/tracing:test_support
+// GN: //src/tracing/test:client_api_integrationtests
 filegroup {
-  name: "perfetto_src_tracing_test_support",
+  name: "perfetto_src_tracing_test_client_api_integrationtests",
   srcs: [
-    "src/tracing/core/trace_writer_for_testing.cc",
+    "src/tracing/test/api_integrationtest.cc",
+    "src/tracing/test/tracing_module.cc",
+    "src/tracing/test/tracing_module2.cc",
   ],
 }
 
-// GN: //src/tracing:tracing
+// GN: //src/tracing/test:test_support
 filegroup {
-  name: "perfetto_src_tracing_tracing",
+  name: "perfetto_src_tracing_test_test_support",
   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",
-  srcs: [
-    "src/tracing/core/id_allocator_unittest.cc",
-    "src/tracing/core/null_trace_writer_unittest.cc",
-    "src/tracing/core/packet_stream_validator_unittest.cc",
-    "src/tracing/core/patch_list_unittest.cc",
-    "src/tracing/core/shared_memory_abi_unittest.cc",
-    "src/tracing/core/shared_memory_arbiter_impl_unittest.cc",
-    "src/tracing/core/startup_trace_writer_unittest.cc",
-    "src/tracing/core/trace_buffer_unittest.cc",
-    "src/tracing/core/trace_packet_unittest.cc",
-    "src/tracing/core/trace_writer_impl_unittest.cc",
-    "src/tracing/core/tracing_service_impl_unittest.cc",
-    "src/tracing/ipc/posix_shared_memory_unittest.cc",
     "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",
   ],
 }
@@ -6656,6 +7401,7 @@
     ":perfetto_protos_perfetto_config_process_stats_lite_gen",
     ":perfetto_protos_perfetto_config_profiling_lite_gen",
     ":perfetto_protos_perfetto_config_sys_stats_lite_gen",
+    ":perfetto_protos_perfetto_config_track_event_lite_gen",
     ":perfetto_protos_perfetto_trace_android_lite_gen",
     ":perfetto_protos_perfetto_trace_chrome_lite_gen",
     ":perfetto_protos_perfetto_trace_filesystem_lite_gen",
@@ -6669,6 +7415,7 @@
     ":perfetto_protos_perfetto_trace_profiling_lite_gen",
     ":perfetto_protos_perfetto_trace_ps_lite_gen",
     ":perfetto_protos_perfetto_trace_sys_stats_lite_gen",
+    ":perfetto_protos_perfetto_trace_system_info_lite_gen",
     ":perfetto_protos_perfetto_trace_track_event_lite_gen",
   ],
   shared_libs: [
@@ -6690,6 +7437,7 @@
     "perfetto_protos_perfetto_config_process_stats_lite_gen_headers",
     "perfetto_protos_perfetto_config_profiling_lite_gen_headers",
     "perfetto_protos_perfetto_config_sys_stats_lite_gen_headers",
+    "perfetto_protos_perfetto_config_track_event_lite_gen_headers",
     "perfetto_protos_perfetto_trace_android_lite_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_lite_gen_headers",
     "perfetto_protos_perfetto_trace_filesystem_lite_gen_headers",
@@ -6703,6 +7451,7 @@
     "perfetto_protos_perfetto_trace_profiling_lite_gen_headers",
     "perfetto_protos_perfetto_trace_ps_lite_gen_headers",
     "perfetto_protos_perfetto_trace_sys_stats_lite_gen_headers",
+    "perfetto_protos_perfetto_trace_system_info_lite_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_lite_gen_headers",
   ],
   export_generated_headers: [
@@ -6716,6 +7465,7 @@
     "perfetto_protos_perfetto_config_process_stats_lite_gen_headers",
     "perfetto_protos_perfetto_config_profiling_lite_gen_headers",
     "perfetto_protos_perfetto_config_sys_stats_lite_gen_headers",
+    "perfetto_protos_perfetto_config_track_event_lite_gen_headers",
     "perfetto_protos_perfetto_trace_android_lite_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_lite_gen_headers",
     "perfetto_protos_perfetto_trace_filesystem_lite_gen_headers",
@@ -6729,6 +7479,7 @@
     "perfetto_protos_perfetto_trace_profiling_lite_gen_headers",
     "perfetto_protos_perfetto_trace_ps_lite_gen_headers",
     "perfetto_protos_perfetto_trace_sys_stats_lite_gen_headers",
+    "perfetto_protos_perfetto_trace_system_info_lite_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_lite_gen_headers",
   ],
   defaults: [
@@ -6747,6 +7498,7 @@
     ":perfetto_include_perfetto_base_base",
     ":perfetto_include_perfetto_ext_base_base",
     ":perfetto_include_perfetto_ext_ipc_ipc",
+    ":perfetto_include_perfetto_ext_trace_processor_export_json",
     ":perfetto_include_perfetto_ext_traced_sys_stats_counters",
     ":perfetto_include_perfetto_ext_traced_traced",
     ":perfetto_include_perfetto_ext_tracing_core_core",
@@ -6779,6 +7531,8 @@
     ":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_track_event_cpp_gen",
+    ":perfetto_protos_perfetto_config_track_event_zero_gen",
     ":perfetto_protos_perfetto_config_zero_gen",
     ":perfetto_protos_perfetto_ipc_cpp_gen",
     ":perfetto_protos_perfetto_ipc_ipc_gen",
@@ -6812,6 +7566,8 @@
     ":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_system_info_cpp_gen",
+    ":perfetto_protos_perfetto_trace_system_info_zero_gen",
     ":perfetto_protos_perfetto_trace_track_event_cpp_gen",
     ":perfetto_protos_perfetto_trace_track_event_zero_gen",
     ":perfetto_src_android_internal_headers",
@@ -6820,7 +7576,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",
@@ -6829,43 +7587,64 @@
     ":perfetto_src_perfetto_cmd_protos_gen",
     ":perfetto_src_perfetto_cmd_trigger_producer",
     ":perfetto_src_perfetto_cmd_unittests",
+    ":perfetto_src_profiling_common_callstack_trie",
+    ":perfetto_src_profiling_common_interner",
+    ":perfetto_src_profiling_common_interning_output",
+    ":perfetto_src_profiling_common_proc_utils",
+    ":perfetto_src_profiling_common_unittests",
+    ":perfetto_src_profiling_common_unwind_support",
     ":perfetto_src_profiling_deobfuscator",
     ":perfetto_src_profiling_memory_client",
     ":perfetto_src_profiling_memory_daemon",
-    ":perfetto_src_profiling_memory_proc_utils",
     ":perfetto_src_profiling_memory_ring_buffer",
     ":perfetto_src_profiling_memory_ring_buffer_unittests",
     ":perfetto_src_profiling_memory_scoped_spinlock",
     ":perfetto_src_profiling_memory_unittests",
     ":perfetto_src_profiling_memory_wire_protocol",
+    ":perfetto_src_profiling_perf_common_types",
+    ":perfetto_src_profiling_perf_proc_descriptors",
     ":perfetto_src_profiling_perf_producer",
     ":perfetto_src_profiling_perf_producer_unittests",
-    ":perfetto_src_profiling_perf_unwind_support",
+    ":perfetto_src_profiling_perf_regs_parsing",
+    ":perfetto_src_profiling_perf_unwinding",
     ":perfetto_src_profiling_unittests",
     ":perfetto_src_protozero_protozero",
     ":perfetto_src_protozero_testing_messages_cpp_gen",
     ":perfetto_src_protozero_testing_messages_lite_gen",
     ":perfetto_src_protozero_testing_messages_zero_gen",
     ":perfetto_src_protozero_unittests",
+    ":perfetto_src_trace_processor_analysis_analysis",
     ":perfetto_src_trace_processor_containers_containers",
     ":perfetto_src_trace_processor_containers_unittests",
     ":perfetto_src_trace_processor_db_lib",
     ":perfetto_src_trace_processor_db_unittests",
-    ":perfetto_src_trace_processor_descriptors",
+    ":perfetto_src_trace_processor_export_json",
+    ":perfetto_src_trace_processor_ftrace_descriptors",
+    ":perfetto_src_trace_processor_importers_common",
+    ":perfetto_src_trace_processor_importers_unittests",
     ":perfetto_src_trace_processor_lib",
     ":perfetto_src_trace_processor_metrics_lib",
     ":perfetto_src_trace_processor_metrics_unittests",
-    ":perfetto_src_trace_processor_protozero_to_text",
     ":perfetto_src_trace_processor_sqlite_sqlite",
     ":perfetto_src_trace_processor_sqlite_unittests",
     ":perfetto_src_trace_processor_storage_full",
     ":perfetto_src_trace_processor_storage_minimal",
+    ":perfetto_src_trace_processor_storage_storage",
     ":perfetto_src_trace_processor_tables_tables",
     ":perfetto_src_trace_processor_tables_unittests",
+    ":perfetto_src_trace_processor_track_event_descriptor",
     ":perfetto_src_trace_processor_types_types",
+    ":perfetto_src_trace_processor_types_unittests",
     ":perfetto_src_trace_processor_unittests",
+    ":perfetto_src_trace_processor_util_descriptors",
+    ":perfetto_src_trace_processor_util_protozero_to_text",
+    ":perfetto_src_trace_processor_util_unittests",
+    ":perfetto_src_trace_processor_util_util",
     ":perfetto_src_traced_probes_android_log_android_log",
     ":perfetto_src_traced_probes_android_log_unittests",
+    ":perfetto_src_traced_probes_common_common",
+    ":perfetto_src_traced_probes_common_test_support",
+    ":perfetto_src_traced_probes_common_unittests",
     ":perfetto_src_traced_probes_data_source",
     ":perfetto_src_traced_probes_filesystem_filesystem",
     ":perfetto_src_traced_probes_filesystem_unittests",
@@ -6878,6 +7657,8 @@
     ":perfetto_src_traced_probes_ftrace_test_messages_zero_gen",
     ":perfetto_src_traced_probes_ftrace_test_support",
     ":perfetto_src_traced_probes_ftrace_unittests",
+    ":perfetto_src_traced_probes_initial_display_state_initial_display_state",
+    ":perfetto_src_traced_probes_initial_display_state_unittests",
     ":perfetto_src_traced_probes_metatrace_metatrace",
     ":perfetto_src_traced_probes_packages_list_packages_list",
     ":perfetto_src_traced_probes_packages_list_unittests",
@@ -6887,14 +7668,23 @@
     ":perfetto_src_traced_probes_ps_unittests",
     ":perfetto_src_traced_probes_sys_stats_sys_stats",
     ":perfetto_src_traced_probes_sys_stats_unittests",
+    ":perfetto_src_traced_probes_system_info_system_info",
+    ":perfetto_src_traced_probes_system_info_unittests",
     ":perfetto_src_traced_probes_unittests",
     ":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: [
@@ -6909,6 +7699,9 @@
     "libgmock",
     "libgtest",
   ],
+  whole_static_libs: [
+    "perfetto_gtest_logcat_printer",
+  ],
   generated_headers: [
     "gen_merged_sql_metrics",
     "perfetto_protos_perfetto_common_cpp_gen_headers",
@@ -6930,6 +7723,8 @@
     "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_track_event_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_track_event_zero_gen_headers",
     "perfetto_protos_perfetto_config_zero_gen_headers",
     "perfetto_protos_perfetto_ipc_cpp_gen_headers",
     "perfetto_protos_perfetto_ipc_ipc_gen_headers",
@@ -6963,6 +7758,8 @@
     "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_system_info_cpp_gen_headers",
+    "perfetto_protos_perfetto_trace_system_info_zero_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_cpp_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
     "perfetto_src_ipc_test_messages_cpp_gen_headers",
@@ -7034,6 +7831,7 @@
   srcs: [
     ":perfetto_include_perfetto_base_base",
     ":perfetto_include_perfetto_ext_base_base",
+    ":perfetto_include_perfetto_ext_trace_processor_export_json",
     ":perfetto_include_perfetto_ext_traced_sys_stats_counters",
     ":perfetto_include_perfetto_protozero_protozero",
     ":perfetto_include_perfetto_trace_processor_basic_types",
@@ -7048,6 +7846,7 @@
     ":perfetto_protos_perfetto_config_process_stats_zero_gen",
     ":perfetto_protos_perfetto_config_profiling_zero_gen",
     ":perfetto_protos_perfetto_config_sys_stats_zero_gen",
+    ":perfetto_protos_perfetto_config_track_event_zero_gen",
     ":perfetto_protos_perfetto_config_zero_gen",
     ":perfetto_protos_perfetto_metrics_android_zero_gen",
     ":perfetto_protos_perfetto_metrics_zero_gen",
@@ -7065,23 +7864,31 @@
     ":perfetto_protos_perfetto_trace_profiling_zero_gen",
     ":perfetto_protos_perfetto_trace_ps_zero_gen",
     ":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
+    ":perfetto_protos_perfetto_trace_system_info_zero_gen",
     ":perfetto_protos_perfetto_trace_track_event_zero_gen",
     ":perfetto_src_base_base",
     ":perfetto_src_profiling_symbolizer_symbolize_database",
     ":perfetto_src_profiling_symbolizer_symbolizer",
     ":perfetto_src_protozero_protozero",
+    ":perfetto_src_trace_processor_analysis_analysis",
     ":perfetto_src_trace_processor_containers_containers",
     ":perfetto_src_trace_processor_db_lib",
-    ":perfetto_src_trace_processor_descriptors",
+    ":perfetto_src_trace_processor_export_json",
+    ":perfetto_src_trace_processor_ftrace_descriptors",
+    ":perfetto_src_trace_processor_importers_common",
     ":perfetto_src_trace_processor_lib",
     ":perfetto_src_trace_processor_metrics_lib",
     ":perfetto_src_trace_processor_sqlite_sqlite",
     ":perfetto_src_trace_processor_storage_full",
     ":perfetto_src_trace_processor_storage_minimal",
+    ":perfetto_src_trace_processor_storage_storage",
     ":perfetto_src_trace_processor_tables_tables",
+    ":perfetto_src_trace_processor_track_event_descriptor",
     ":perfetto_src_trace_processor_types_types",
-    "src/trace_processor/proto_to_json.cc",
+    ":perfetto_src_trace_processor_util_descriptors",
+    ":perfetto_src_trace_processor_util_util",
     "src/trace_processor/trace_processor_shell.cc",
+    "src/trace_processor/util/proto_to_json.cc",
   ],
   static_libs: [
     "libprotoc",
@@ -7099,6 +7906,7 @@
     "perfetto_protos_perfetto_config_process_stats_zero_gen_headers",
     "perfetto_protos_perfetto_config_profiling_zero_gen_headers",
     "perfetto_protos_perfetto_config_sys_stats_zero_gen_headers",
+    "perfetto_protos_perfetto_config_track_event_zero_gen_headers",
     "perfetto_protos_perfetto_config_zero_gen_headers",
     "perfetto_protos_perfetto_metrics_android_zero_gen_headers",
     "perfetto_protos_perfetto_metrics_zero_gen_headers",
@@ -7116,6 +7924,7 @@
     "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
     "perfetto_protos_perfetto_trace_ps_zero_gen_headers",
     "perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_system_info_zero_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
   ],
   defaults: [
@@ -7126,11 +7935,6 @@
     "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
     "-DHAVE_HIDDEN",
   ],
-  dist: {
-    targets: [
-      "sdk_repo",
-    ],
-  },
   stl: "libc++_static",
 }
 
@@ -7140,6 +7944,7 @@
   srcs: [
     ":perfetto_include_perfetto_base_base",
     ":perfetto_include_perfetto_ext_base_base",
+    ":perfetto_include_perfetto_ext_trace_processor_export_json",
     ":perfetto_include_perfetto_ext_traced_sys_stats_counters",
     ":perfetto_include_perfetto_profiling_deobfuscator",
     ":perfetto_include_perfetto_profiling_pprof_builder",
@@ -7156,6 +7961,7 @@
     ":perfetto_protos_perfetto_config_process_stats_zero_gen",
     ":perfetto_protos_perfetto_config_profiling_zero_gen",
     ":perfetto_protos_perfetto_config_sys_stats_zero_gen",
+    ":perfetto_protos_perfetto_config_track_event_zero_gen",
     ":perfetto_protos_perfetto_config_zero_gen",
     ":perfetto_protos_perfetto_metrics_android_zero_gen",
     ":perfetto_protos_perfetto_metrics_zero_gen",
@@ -7173,6 +7979,7 @@
     ":perfetto_protos_perfetto_trace_profiling_zero_gen",
     ":perfetto_protos_perfetto_trace_ps_zero_gen",
     ":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
+    ":perfetto_protos_perfetto_trace_system_info_zero_gen",
     ":perfetto_protos_perfetto_trace_track_event_zero_gen",
     ":perfetto_protos_third_party_pprof_zero_gen",
     ":perfetto_src_base_base",
@@ -7180,16 +7987,23 @@
     ":perfetto_src_profiling_symbolizer_symbolize_database",
     ":perfetto_src_profiling_symbolizer_symbolizer",
     ":perfetto_src_protozero_protozero",
+    ":perfetto_src_trace_processor_analysis_analysis",
     ":perfetto_src_trace_processor_containers_containers",
     ":perfetto_src_trace_processor_db_lib",
-    ":perfetto_src_trace_processor_descriptors",
+    ":perfetto_src_trace_processor_export_json",
+    ":perfetto_src_trace_processor_ftrace_descriptors",
+    ":perfetto_src_trace_processor_importers_common",
     ":perfetto_src_trace_processor_lib",
     ":perfetto_src_trace_processor_metrics_lib",
     ":perfetto_src_trace_processor_sqlite_sqlite",
     ":perfetto_src_trace_processor_storage_full",
     ":perfetto_src_trace_processor_storage_minimal",
+    ":perfetto_src_trace_processor_storage_storage",
     ":perfetto_src_trace_processor_tables_tables",
+    ":perfetto_src_trace_processor_track_event_descriptor",
     ":perfetto_src_trace_processor_types_types",
+    ":perfetto_src_trace_processor_util_descriptors",
+    ":perfetto_src_trace_processor_util_util",
     ":perfetto_tools_trace_to_text_common",
     ":perfetto_tools_trace_to_text_full",
     ":perfetto_tools_trace_to_text_pprofbuilder",
@@ -7213,6 +8027,7 @@
     "perfetto_protos_perfetto_config_process_stats_zero_gen_headers",
     "perfetto_protos_perfetto_config_profiling_zero_gen_headers",
     "perfetto_protos_perfetto_config_sys_stats_zero_gen_headers",
+    "perfetto_protos_perfetto_config_track_event_zero_gen_headers",
     "perfetto_protos_perfetto_config_zero_gen_headers",
     "perfetto_protos_perfetto_metrics_android_zero_gen_headers",
     "perfetto_protos_perfetto_metrics_zero_gen_headers",
@@ -7230,6 +8045,7 @@
     "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
     "perfetto_protos_perfetto_trace_ps_zero_gen_headers",
     "perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_system_info_zero_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
     "perfetto_protos_third_party_pprof_zero_gen_headers",
   ],
@@ -7271,6 +8087,7 @@
     ":perfetto_include_perfetto_ext_ipc_ipc",
     ":perfetto_include_perfetto_ext_tracing_core_core",
     ":perfetto_include_perfetto_ext_tracing_ipc_ipc",
+    ":perfetto_include_perfetto_profiling_normalize",
     ":perfetto_include_perfetto_protozero_protozero",
     ":perfetto_include_perfetto_tracing_core_core",
     ":perfetto_include_perfetto_tracing_core_forward_decls",
@@ -7294,6 +8111,8 @@
     ":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_track_event_cpp_gen",
+    ":perfetto_protos_perfetto_config_track_event_zero_gen",
     ":perfetto_protos_perfetto_config_zero_gen",
     ":perfetto_protos_perfetto_ipc_cpp_gen",
     ":perfetto_protos_perfetto_ipc_ipc_gen",
@@ -7311,17 +8130,29 @@
     ":perfetto_protos_perfetto_trace_profiling_zero_gen",
     ":perfetto_protos_perfetto_trace_ps_zero_gen",
     ":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
+    ":perfetto_protos_perfetto_trace_system_info_zero_gen",
     ":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_common_callstack_trie",
+    ":perfetto_src_profiling_common_interner",
+    ":perfetto_src_profiling_common_interning_output",
+    ":perfetto_src_profiling_common_proc_utils",
+    ":perfetto_src_profiling_common_unwind_support",
+    ":perfetto_src_profiling_perf_common_types",
+    ":perfetto_src_profiling_perf_proc_descriptors",
     ":perfetto_src_profiling_perf_producer",
+    ":perfetto_src_profiling_perf_regs_parsing",
     ":perfetto_src_profiling_perf_traced_perf_main",
-    ":perfetto_src_profiling_perf_unwind_support",
+    ":perfetto_src_profiling_perf_unwinding",
     ":perfetto_src_protozero_protozero",
     ":perfetto_src_tracing_common",
-    ":perfetto_src_tracing_ipc",
-    ":perfetto_src_tracing_tracing",
+    ":perfetto_src_tracing_core_core",
+    ":perfetto_src_tracing_core_service",
+    ":perfetto_src_tracing_ipc_common",
+    ":perfetto_src_tracing_ipc_producer_producer",
     "src/profiling/perf/main.cc",
   ],
   shared_libs: [
@@ -7330,6 +8161,9 @@
     "libprocinfo",
     "libunwindstack",
   ],
+  init_rc: [
+    "traced_perf.rc",
+  ],
   generated_headers: [
     "perfetto_protos_perfetto_common_cpp_gen_headers",
     "perfetto_protos_perfetto_common_zero_gen_headers",
@@ -7350,6 +8184,8 @@
     "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_track_event_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_track_event_zero_gen_headers",
     "perfetto_protos_perfetto_config_zero_gen_headers",
     "perfetto_protos_perfetto_ipc_cpp_gen_headers",
     "perfetto_protos_perfetto_ipc_ipc_gen_headers",
@@ -7367,6 +8203,7 @@
     "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
     "perfetto_protos_perfetto_trace_ps_zero_gen_headers",
     "perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_system_info_zero_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
   ],
   defaults: [
@@ -7397,6 +8234,7 @@
   ],
   required: [
     "libperfetto_android_internal",
+    "traced_perf",
     "trigger_perfetto",
   ],
 }
@@ -7434,6 +8272,8 @@
     ":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_track_event_cpp_gen",
+    ":perfetto_protos_perfetto_config_track_event_zero_gen",
     ":perfetto_protos_perfetto_config_zero_gen",
     ":perfetto_protos_perfetto_ipc_cpp_gen",
     ":perfetto_protos_perfetto_ipc_ipc_gen",
@@ -7451,17 +8291,20 @@
     ":perfetto_protos_perfetto_trace_profiling_zero_gen",
     ":perfetto_protos_perfetto_trace_ps_zero_gen",
     ":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
+    ":perfetto_protos_perfetto_trace_system_info_zero_gen",
     ":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: [
@@ -7487,6 +8330,8 @@
     "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_track_event_cpp_gen_headers",
+    "perfetto_protos_perfetto_config_track_event_zero_gen_headers",
     "perfetto_protos_perfetto_config_zero_gen_headers",
     "perfetto_protos_perfetto_ipc_cpp_gen_headers",
     "perfetto_protos_perfetto_ipc_ipc_gen_headers",
@@ -7504,6 +8349,7 @@
     "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
     "perfetto_protos_perfetto_trace_ps_zero_gen_headers",
     "perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_system_info_zero_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
     "perfetto_src_perfetto_cmd_protos_gen_headers",
   ],
@@ -7595,4 +8441,30 @@
     out: [
         "statslog_perfetto.cpp",
     ],
+}
+
+cc_genrule {
+  name: "trace_processor_shell.stripped",
+  device_supported: false,
+  host_supported: true,
+  cmd: "$(location tools/strip_android_host_binary.py) $(in) -o $(out)",
+  enabled: false,
+  compile_multilib: "64",
+  tool_files: [
+    "tools/strip_android_host_binary.py",
+  ],
+  dist: {
+    targets: [
+      "sdk_repo",
+    ],
+  },
+  target: {
+    linux: {
+      out: [
+        "trace_processor_shell.stripped",
+      ],
+      srcs: [":trace_processor_shell"],
+      enabled: true,
+    },
+  },
 }
\ No newline at end of file
diff --git a/Android.bp.extras b/Android.bp.extras
index 65a81f4..a4615eb 100644
--- a/Android.bp.extras
+++ b/Android.bp.extras
@@ -78,3 +78,29 @@
         "statslog_perfetto.cpp",
     ],
 }
+
+cc_genrule {
+  name: "trace_processor_shell.stripped",
+  device_supported: false,
+  host_supported: true,
+  cmd: "$(location tools/strip_android_host_binary.py) $(in) -o $(out)",
+  enabled: false,
+  compile_multilib: "64",
+  tool_files: [
+    "tools/strip_android_host_binary.py",
+  ],
+  dist: {
+    targets: [
+      "sdk_repo",
+    ],
+  },
+  target: {
+    linux: {
+      out: [
+        "trace_processor_shell.stripped",
+      ],
+      srcs: [":trace_processor_shell"],
+      enabled: true,
+    },
+  },
+}
diff --git a/BUILD b/BUILD
index 3df1dca..33c1a8e 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",
@@ -135,6 +137,8 @@
         ":protos_perfetto_config_process_stats_zero",
         ":protos_perfetto_config_profiling_zero",
         ":protos_perfetto_config_sys_stats_zero",
+        ":protos_perfetto_config_track_event_cpp",
+        ":protos_perfetto_config_track_event_zero",
         ":protos_perfetto_config_zero",
         ":protos_perfetto_trace_android_zero",
         ":protos_perfetto_trace_chrome_zero",
@@ -149,6 +153,7 @@
         ":protos_perfetto_trace_profiling_zero",
         ":protos_perfetto_trace_ps_zero",
         ":protos_perfetto_trace_sys_stats_zero",
+        ":protos_perfetto_trace_system_info_zero",
         ":protos_perfetto_trace_track_event_zero",
     ],
 )
@@ -161,14 +166,18 @@
         ":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",
+        ":src_traced_probes_common_common",
         ":src_traced_probes_data_source",
         ":src_traced_probes_filesystem_filesystem",
         ":src_traced_probes_ftrace_format_parser",
         ":src_traced_probes_ftrace_ftrace",
+        ":src_traced_probes_initial_display_state_initial_display_state",
         ":src_traced_probes_metatrace_metatrace",
         ":src_traced_probes_packages_list_packages_list",
         ":src_traced_probes_power_power",
@@ -176,11 +185,16 @@
         ":src_traced_probes_probes_src",
         ":src_traced_probes_ps_ps",
         ":src_traced_probes_sys_stats_sys_stats",
+        ":src_traced_probes_system_info_system_info",
         ":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",
@@ -216,6 +230,8 @@
         ":protos_perfetto_config_profiling_zero",
         ":protos_perfetto_config_sys_stats_cpp",
         ":protos_perfetto_config_sys_stats_zero",
+        ":protos_perfetto_config_track_event_cpp",
+        ":protos_perfetto_config_track_event_zero",
         ":protos_perfetto_config_zero",
         ":protos_perfetto_ipc_cpp",
         ":protos_perfetto_ipc_ipc",
@@ -233,6 +249,7 @@
         ":protos_perfetto_trace_profiling_zero",
         ":protos_perfetto_trace_ps_zero",
         ":protos_perfetto_trace_sys_stats_zero",
+        ":protos_perfetto_trace_system_info_zero",
         ":protos_perfetto_trace_track_event_zero",
     ],
     linkstatic = True,
@@ -276,10 +293,12 @@
         "include/perfetto/ext/base/string_utils.h",
         "include/perfetto/ext/base/string_view.h",
         "include/perfetto/ext/base/string_writer.h",
+        "include/perfetto/ext/base/subprocess.h",
         "include/perfetto/ext/base/temp_file.h",
         "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",
@@ -347,8 +366,6 @@
         "include/perfetto/ext/tracing/core/shared_memory_abi.h",
         "include/perfetto/ext/tracing/core/shared_memory_arbiter.h",
         "include/perfetto/ext/tracing/core/slice.h",
-        "include/perfetto/ext/tracing/core/startup_trace_writer.h",
-        "include/perfetto/ext/tracing/core/startup_trace_writer_registry.h",
         "include/perfetto/ext/tracing/core/trace_packet.h",
         "include/perfetto/ext/tracing/core/trace_stats.h",
         "include/perfetto/ext/tracing/core/trace_writer.h",
@@ -445,6 +462,7 @@
         "include/perfetto/tracing/core/data_source_config.h",
         "include/perfetto/tracing/core/data_source_descriptor.h",
         "include/perfetto/tracing/core/trace_config.h",
+        "include/perfetto/tracing/core/tracing_service_capabilities.h",
         "include/perfetto/tracing/core/tracing_service_state.h",
     ],
 )
@@ -467,6 +485,8 @@
         "include/perfetto/tracing/event_context.h",
         "include/perfetto/tracing/internal/basic_types.h",
         "include/perfetto/tracing/internal/data_source_internal.h",
+        "include/perfetto/tracing/internal/in_process_tracing_backend.h",
+        "include/perfetto/tracing/internal/system_tracing_backend.h",
         "include/perfetto/tracing/internal/tracing_muxer.h",
         "include/perfetto/tracing/internal/tracing_tls.h",
         "include/perfetto/tracing/internal/track_event_data_source.h",
@@ -520,6 +540,7 @@
         "src/base/string_splitter.cc",
         "src/base/string_utils.cc",
         "src/base/string_view.cc",
+        "src/base/subprocess.cc",
         "src/base/temp_file.cc",
         "src/base/thread_checker.cc",
         "src/base/thread_task_runner.cc",
@@ -540,19 +561,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",
     ],
 )
 
@@ -636,6 +671,15 @@
     ],
 )
 
+# GN target: //src/trace_processor/analysis:analysis
+filegroup(
+    name = "src_trace_processor_analysis_analysis",
+    srcs = [
+        "src/trace_processor/analysis/describe_slice.cc",
+        "src/trace_processor/analysis/describe_slice.h",
+    ],
+)
+
 # GN target: //src/trace_processor/containers:containers
 filegroup(
     name = "src_trace_processor_containers_containers",
@@ -647,6 +691,7 @@
         "src/trace_processor/containers/null_term_string_view.h",
         "src/trace_processor/containers/row_map.cc",
         "src/trace_processor/containers/row_map.h",
+        "src/trace_processor/containers/sparse_vector.cc",
         "src/trace_processor/containers/sparse_vector.h",
         "src/trace_processor/containers/string_pool.cc",
         "src/trace_processor/containers/string_pool.h",
@@ -663,6 +708,28 @@
         "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",
+    ],
+)
+
+# GN target: //src/trace_processor/importers:common
+filegroup(
+    name = "src_trace_processor_importers_common",
+    srcs = [
+        "src/trace_processor/importers/common/args_tracker.cc",
+        "src/trace_processor/importers/common/args_tracker.h",
+        "src/trace_processor/importers/common/clock_tracker.cc",
+        "src/trace_processor/importers/common/clock_tracker.h",
+        "src/trace_processor/importers/common/event_tracker.cc",
+        "src/trace_processor/importers/common/event_tracker.h",
+        "src/trace_processor/importers/common/global_args_tracker.cc",
+        "src/trace_processor/importers/common/global_args_tracker.h",
+        "src/trace_processor/importers/common/process_tracker.cc",
+        "src/trace_processor/importers/common/process_tracker.h",
+        "src/trace_processor/importers/common/slice_tracker.cc",
+        "src/trace_processor/importers/common/slice_tracker.h",
+        "src/trace_processor/importers/common/track_tracker.cc",
+        "src/trace_processor/importers/common/track_tracker.h",
     ],
 )
 
@@ -681,10 +748,15 @@
         "src/trace_processor/metrics/android/android_package_list.sql",
         "src/trace_processor/metrics/android/android_powrails.sql",
         "src/trace_processor/metrics/android/android_startup.sql",
-        "src/trace_processor/metrics/android/android_startup_cpu.sql",
         "src/trace_processor/metrics/android/android_startup_launches.sql",
+        "src/trace_processor/metrics/android/android_task_names.sql",
         "src/trace_processor/metrics/android/android_task_state.sql",
+        "src/trace_processor/metrics/android/android_thread_time_in_state.sql",
+        "src/trace_processor/metrics/android/cpu_info.sql",
+        "src/trace_processor/metrics/android/display_metrics.sql",
         "src/trace_processor/metrics/android/heap_profile_callsites.sql",
+        "src/trace_processor/metrics/android/hsc_startups.sql",
+        "src/trace_processor/metrics/android/java_heap_histogram.sql",
         "src/trace_processor/metrics/android/java_heap_stats.sql",
         "src/trace_processor/metrics/android/mem_stats_priority_breakdown.sql",
         "src/trace_processor/metrics/android/process_mem.sql",
@@ -741,14 +813,36 @@
     srcs = [
         "src/trace_processor/sqlite/db_sqlite_table.cc",
         "src/trace_processor/sqlite/db_sqlite_table.h",
+        "src/trace_processor/sqlite/query_cache.h",
         "src/trace_processor/sqlite/query_constraints.cc",
         "src/trace_processor/sqlite/query_constraints.h",
         "src/trace_processor/sqlite/scoped_db.h",
+        "src/trace_processor/sqlite/span_join_operator_table.cc",
+        "src/trace_processor/sqlite/span_join_operator_table.h",
+        "src/trace_processor/sqlite/sql_stats_table.cc",
+        "src/trace_processor/sqlite/sql_stats_table.h",
         "src/trace_processor/sqlite/sqlite3_str_split.cc",
         "src/trace_processor/sqlite/sqlite3_str_split.h",
+        "src/trace_processor/sqlite/sqlite_raw_table.cc",
+        "src/trace_processor/sqlite/sqlite_raw_table.h",
         "src/trace_processor/sqlite/sqlite_table.cc",
         "src/trace_processor/sqlite/sqlite_table.h",
         "src/trace_processor/sqlite/sqlite_utils.h",
+        "src/trace_processor/sqlite/stats_table.cc",
+        "src/trace_processor/sqlite/stats_table.h",
+        "src/trace_processor/sqlite/window_operator_table.cc",
+        "src/trace_processor/sqlite/window_operator_table.h",
+    ],
+)
+
+# GN target: //src/trace_processor/storage:storage
+filegroup(
+    name = "src_trace_processor_storage_storage",
+    srcs = [
+        "src/trace_processor/storage/metadata.h",
+        "src/trace_processor/storage/stats.h",
+        "src/trace_processor/storage/trace_storage.cc",
+        "src/trace_processor/storage/trace_storage.h",
     ],
 )
 
@@ -763,6 +857,7 @@
         "src/trace_processor/tables/metadata_tables.h",
         "src/trace_processor/tables/profiler_tables.h",
         "src/trace_processor/tables/slice_tables.h",
+        "src/trace_processor/tables/table_destructors.cc",
         "src/trace_processor/tables/track_tables.h",
     ],
 )
@@ -771,19 +866,32 @@
 filegroup(
     name = "src_trace_processor_types_types",
     srcs = [
+        "src/trace_processor/types/destructible.cc",
+        "src/trace_processor/types/destructible.h",
         "src/trace_processor/types/gfp_flags.cc",
         "src/trace_processor/types/gfp_flags.h",
+        "src/trace_processor/types/task_state.cc",
+        "src/trace_processor/types/task_state.h",
+        "src/trace_processor/types/trace_processor_context.h",
         "src/trace_processor/types/variadic.cc",
         "src/trace_processor/types/variadic.h",
     ],
 )
 
-# GN target: //src/trace_processor:descriptors
+# GN target: //src/trace_processor/util:descriptors
 filegroup(
-    name = "src_trace_processor_descriptors",
+    name = "src_trace_processor_util_descriptors",
     srcs = [
-        "src/trace_processor/descriptors.cc",
-        "src/trace_processor/descriptors.h",
+        "src/trace_processor/util/descriptors.cc",
+        "src/trace_processor/util/descriptors.h",
+    ],
+)
+
+# GN target: //src/trace_processor/util:util
+filegroup(
+    name = "src_trace_processor_util_util",
+    srcs = [
+        "src/trace_processor/util/status_macros.h",
     ],
 )
 
@@ -796,38 +904,31 @@
     ],
 )
 
+# GN target: //src/trace_processor:ftrace_descriptors
+filegroup(
+    name = "src_trace_processor_ftrace_descriptors",
+    srcs = [
+        "src/trace_processor/importers/ftrace/ftrace_descriptors.cc",
+        "src/trace_processor/importers/ftrace/ftrace_descriptors.h",
+    ],
+)
+
 # GN target: //src/trace_processor:lib
 filegroup(
     name = "src_trace_processor_lib",
     srcs = [
-        "src/trace_processor/filtered_row_index.cc",
-        "src/trace_processor/filtered_row_index.h",
+        "src/trace_processor/dynamic/describe_slice_generator.cc",
+        "src/trace_processor/dynamic/describe_slice_generator.h",
+        "src/trace_processor/dynamic/experimental_counter_dur_generator.cc",
+        "src/trace_processor/dynamic/experimental_counter_dur_generator.h",
+        "src/trace_processor/dynamic/experimental_flamegraph_generator.cc",
+        "src/trace_processor/dynamic/experimental_flamegraph_generator.h",
+        "src/trace_processor/dynamic/experimental_slice_layout_generator.cc",
+        "src/trace_processor/dynamic/experimental_slice_layout_generator.h",
         "src/trace_processor/read_trace.cc",
-        "src/trace_processor/row_iterators.cc",
-        "src/trace_processor/row_iterators.h",
-        "src/trace_processor/sched_slice_table.cc",
-        "src/trace_processor/sched_slice_table.h",
-        "src/trace_processor/span_join_operator_table.cc",
-        "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",
-        "src/trace_processor/storage_columns.h",
-        "src/trace_processor/storage_schema.cc",
-        "src/trace_processor/storage_schema.h",
-        "src/trace_processor/storage_table.cc",
-        "src/trace_processor/storage_table.h",
         "src/trace_processor/trace_processor.cc",
         "src/trace_processor/trace_processor_impl.cc",
         "src/trace_processor/trace_processor_impl.h",
-        "src/trace_processor/window_operator_table.cc",
-        "src/trace_processor/window_operator_table.h",
     ],
 )
 
@@ -835,10 +936,10 @@
 filegroup(
     name = "src_trace_processor_storage_full",
     srcs = [
+        "src/trace_processor/importers/additional_modules.cc",
+        "src/trace_processor/importers/additional_modules.h",
         "src/trace_processor/importers/ftrace/binder_tracker.cc",
         "src/trace_processor/importers/ftrace/binder_tracker.h",
-        "src/trace_processor/importers/ftrace/ftrace_descriptors.cc",
-        "src/trace_processor/importers/ftrace/ftrace_descriptors.h",
         "src/trace_processor/importers/ftrace/ftrace_module_impl.cc",
         "src/trace_processor/importers/ftrace/ftrace_module_impl.h",
         "src/trace_processor/importers/ftrace/ftrace_parser.cc",
@@ -849,10 +950,26 @@
         "src/trace_processor/importers/ftrace/rss_stat_tracker.h",
         "src/trace_processor/importers/ftrace/sched_event_tracker.cc",
         "src/trace_processor/importers/ftrace/sched_event_tracker.h",
+        "src/trace_processor/importers/fuchsia/fuchsia_record.cc",
+        "src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc",
+        "src/trace_processor/importers/fuchsia/fuchsia_trace_parser.h",
+        "src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc",
+        "src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h",
+        "src/trace_processor/importers/fuchsia/fuchsia_trace_utils.cc",
+        "src/trace_processor/importers/gzip/gzip_trace_parser.cc",
+        "src/trace_processor/importers/gzip/gzip_trace_parser.h",
+        "src/trace_processor/importers/json/json_trace_parser.cc",
+        "src/trace_processor/importers/json/json_trace_parser.h",
+        "src/trace_processor/importers/json/json_trace_tokenizer.cc",
+        "src/trace_processor/importers/json/json_trace_tokenizer.h",
+        "src/trace_processor/importers/json/json_tracker.cc",
+        "src/trace_processor/importers/json/json_tracker.h",
         "src/trace_processor/importers/proto/android_probes_module.cc",
         "src/trace_processor/importers/proto/android_probes_module.h",
         "src/trace_processor/importers/proto/android_probes_parser.cc",
         "src/trace_processor/importers/proto/android_probes_parser.h",
+        "src/trace_processor/importers/proto/android_probes_tracker.cc",
+        "src/trace_processor/importers/proto/android_probes_tracker.h",
         "src/trace_processor/importers/proto/graphics_event_module.cc",
         "src/trace_processor/importers/proto/graphics_event_module.h",
         "src/trace_processor/importers/proto/graphics_event_parser.cc",
@@ -869,17 +986,20 @@
         "src/trace_processor/importers/proto/system_probes_parser.h",
         "src/trace_processor/importers/proto/vulkan_memory_tracker.cc",
         "src/trace_processor/importers/proto/vulkan_memory_tracker.h",
+        "src/trace_processor/importers/syscalls/syscall_tracker.cc",
+        "src/trace_processor/importers/syscalls/syscalls_aarch32.h",
+        "src/trace_processor/importers/syscalls/syscalls_aarch64.h",
+        "src/trace_processor/importers/syscalls/syscalls_armeabi.h",
+        "src/trace_processor/importers/syscalls/syscalls_x86.h",
+        "src/trace_processor/importers/syscalls/syscalls_x86_64.h",
+        "src/trace_processor/importers/systrace/systrace_line_parser.cc",
+        "src/trace_processor/importers/systrace/systrace_line_parser.h",
+        "src/trace_processor/importers/systrace/systrace_line_tokenizer.cc",
+        "src/trace_processor/importers/systrace/systrace_line_tokenizer.h",
         "src/trace_processor/importers/systrace/systrace_parser.cc",
         "src/trace_processor/importers/systrace/systrace_parser.h",
         "src/trace_processor/importers/systrace/systrace_trace_parser.cc",
         "src/trace_processor/importers/systrace/systrace_trace_parser.h",
-        "src/trace_processor/register_additional_modules.cc",
-        "src/trace_processor/register_additional_modules.h",
-        "src/trace_processor/syscall_tracker.cc",
-        "src/trace_processor/syscalls_aarch32.h",
-        "src/trace_processor/syscalls_aarch64.h",
-        "src/trace_processor/syscalls_armeabi.h",
-        "src/trace_processor/syscalls_x86_64.h",
     ],
 )
 
@@ -887,46 +1007,35 @@
 filegroup(
     name = "src_trace_processor_storage_minimal",
     srcs = [
-        "src/trace_processor/args_tracker.cc",
-        "src/trace_processor/args_tracker.h",
         "src/trace_processor/chunked_trace_reader.h",
-        "src/trace_processor/clock_tracker.cc",
-        "src/trace_processor/clock_tracker.h",
-        "src/trace_processor/destructible.cc",
-        "src/trace_processor/destructible.h",
-        "src/trace_processor/event_tracker.cc",
-        "src/trace_processor/event_tracker.h",
         "src/trace_processor/forwarding_trace_parser.cc",
         "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",
-        "src/trace_processor/heap_profile_tracker.h",
+        "src/trace_processor/importers/default_modules.cc",
+        "src/trace_processor/importers/default_modules.h",
         "src/trace_processor/importers/ftrace/ftrace_module.cc",
         "src/trace_processor/importers/ftrace/ftrace_module.h",
-        "src/trace_processor/importers/fuchsia/fuchsia_record.cc",
         "src/trace_processor/importers/fuchsia/fuchsia_record.h",
-        "src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc",
-        "src/trace_processor/importers/fuchsia/fuchsia_trace_parser.h",
-        "src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc",
-        "src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h",
-        "src/trace_processor/importers/fuchsia/fuchsia_trace_utils.cc",
         "src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h",
-        "src/trace_processor/importers/json/json_trace_parser.cc",
-        "src/trace_processor/importers/json/json_trace_parser.h",
-        "src/trace_processor/importers/json/json_trace_tokenizer.cc",
-        "src/trace_processor/importers/json/json_trace_tokenizer.h",
-        "src/trace_processor/importers/json/json_trace_utils.cc",
-        "src/trace_processor/importers/json/json_trace_utils.h",
+        "src/trace_processor/importers/gzip/gzip_utils.cc",
+        "src/trace_processor/importers/gzip/gzip_utils.h",
+        "src/trace_processor/importers/json/json_utils.cc",
+        "src/trace_processor/importers/json/json_utils.h",
+        "src/trace_processor/importers/ninja/ninja_log_parser.cc",
+        "src/trace_processor/importers/ninja/ninja_log_parser.h",
         "src/trace_processor/importers/proto/args_table_utils.cc",
         "src/trace_processor/importers/proto/args_table_utils.h",
-        "src/trace_processor/importers/proto/chrome_compositor_scheduler_state.descriptor.h",
+        "src/trace_processor/importers/proto/heap_profile_tracker.cc",
+        "src/trace_processor/importers/proto/heap_profile_tracker.h",
+        "src/trace_processor/importers/proto/metadata_tracker.cc",
+        "src/trace_processor/importers/proto/metadata_tracker.h",
         "src/trace_processor/importers/proto/packet_sequence_state.cc",
         "src/trace_processor/importers/proto/packet_sequence_state.h",
+        "src/trace_processor/importers/proto/perf_sample_tracker.cc",
+        "src/trace_processor/importers/proto/perf_sample_tracker.h",
+        "src/trace_processor/importers/proto/profile_module.cc",
+        "src/trace_processor/importers/proto/profile_module.h",
+        "src/trace_processor/importers/proto/profile_packet_utils.cc",
+        "src/trace_processor/importers/proto/profile_packet_utils.h",
         "src/trace_processor/importers/proto/proto_importer_module.cc",
         "src/trace_processor/importers/proto/proto_importer_module.h",
         "src/trace_processor/importers/proto/proto_incremental_state.h",
@@ -934,41 +1043,37 @@
         "src/trace_processor/importers/proto/proto_trace_parser.h",
         "src/trace_processor/importers/proto/proto_trace_tokenizer.cc",
         "src/trace_processor/importers/proto/proto_trace_tokenizer.h",
+        "src/trace_processor/importers/proto/stack_profile_tracker.cc",
+        "src/trace_processor/importers/proto/stack_profile_tracker.h",
         "src/trace_processor/importers/proto/track_event_module.cc",
         "src/trace_processor/importers/proto/track_event_module.h",
         "src/trace_processor/importers/proto/track_event_parser.cc",
         "src/trace_processor/importers/proto/track_event_parser.h",
         "src/trace_processor/importers/proto/track_event_tokenizer.cc",
         "src/trace_processor/importers/proto/track_event_tokenizer.h",
-        "src/trace_processor/metadata.h",
-        "src/trace_processor/metadata_tracker.cc",
-        "src/trace_processor/metadata_tracker.h",
-        "src/trace_processor/process_tracker.cc",
-        "src/trace_processor/process_tracker.h",
-        "src/trace_processor/slice_tracker.cc",
-        "src/trace_processor/slice_tracker.h",
-        "src/trace_processor/stack_profile_tracker.cc",
-        "src/trace_processor/stack_profile_tracker.h",
-        "src/trace_processor/stats.h",
-        "src/trace_processor/syscall_tracker.h",
+        "src/trace_processor/importers/syscalls/syscall_tracker.h",
+        "src/trace_processor/importers/systrace/systrace_line.h",
         "src/trace_processor/timestamped_trace_piece.h",
         "src/trace_processor/trace_blob_view.h",
         "src/trace_processor/trace_parser.h",
         "src/trace_processor/trace_processor_context.cc",
-        "src/trace_processor/trace_processor_context.h",
         "src/trace_processor/trace_processor_storage.cc",
         "src/trace_processor/trace_processor_storage_impl.cc",
         "src/trace_processor/trace_processor_storage_impl.h",
         "src/trace_processor/trace_sorter.cc",
         "src/trace_processor/trace_sorter.h",
-        "src/trace_processor/trace_storage.cc",
-        "src/trace_processor/trace_storage.h",
-        "src/trace_processor/track_tracker.cc",
-        "src/trace_processor/track_tracker.h",
         "src/trace_processor/virtual_destructors.cc",
     ],
 )
 
+# GN target: //src/trace_processor:track_event_descriptor
+filegroup(
+    name = "src_trace_processor_track_event_descriptor",
+    srcs = [
+        "src/trace_processor/importers/proto/track_event.descriptor.h",
+    ],
+)
+
 # GN target: //src/traced/probes/android_log:android_log
 filegroup(
     name = "src_traced_probes_android_log_android_log",
@@ -978,6 +1083,15 @@
     ],
 )
 
+# GN target: //src/traced/probes/common:common
+filegroup(
+    name = "src_traced_probes_common_common",
+    srcs = [
+        "src/traced/probes/common/cpu_freq_info.cc",
+        "src/traced/probes/common/cpu_freq_info.h",
+    ],
+)
+
 # GN target: //src/traced/probes/filesystem:filesystem
 filegroup(
     name = "src_traced_probes_filesystem_filesystem",
@@ -1020,6 +1134,8 @@
         "src/traced/probes/ftrace/cpu_reader.h",
         "src/traced/probes/ftrace/cpu_stats_parser.cc",
         "src/traced/probes/ftrace/cpu_stats_parser.h",
+        "src/traced/probes/ftrace/discover_vendor_tracepoints.cc",
+        "src/traced/probes/ftrace/discover_vendor_tracepoints.h",
         "src/traced/probes/ftrace/event_info.cc",
         "src/traced/probes/ftrace/event_info.h",
         "src/traced/probes/ftrace/event_info_constants.cc",
@@ -1042,6 +1158,15 @@
     ],
 )
 
+# GN target: //src/traced/probes/initial_display_state:initial_display_state
+filegroup(
+    name = "src_traced_probes_initial_display_state_initial_display_state",
+    srcs = [
+        "src/traced/probes/initial_display_state/initial_display_state_data_source.cc",
+        "src/traced/probes/initial_display_state/initial_display_state_data_source.h",
+    ],
+)
+
 # GN target: //src/traced/probes/metatrace:metatrace
 filegroup(
     name = "src_traced_probes_metatrace_metatrace",
@@ -1087,6 +1212,15 @@
     ],
 )
 
+# GN target: //src/traced/probes/system_info:system_info
+filegroup(
+    name = "src_traced_probes_system_info_system_info",
+    srcs = [
+        "src/traced/probes/system_info/system_info_data_source.cc",
+        "src/traced/probes/system_info/system_info_data_source.h",
+    ],
+)
+
 # GN target: //src/traced/probes:data_source
 filegroup(
     name = "src_traced_probes_data_source",
@@ -1123,17 +1257,98 @@
     ],
 )
 
-# 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/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/memfd.cc",
+        "src/tracing/ipc/memfd.h",
+        "src/tracing/ipc/posix_shared_memory.cc",
+        "src/tracing/ipc/posix_shared_memory.h",
+    ],
+)
+
+# GN target: //src/tracing:client_api_without_backends
+filegroup(
+    name = "src_tracing_client_api_without_backends",
     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",
@@ -1141,6 +1356,7 @@
         "src/tracing/tracing.cc",
         "src/tracing/track.cc",
         "src/tracing/track_event_category_registry.cc",
+        "src/tracing/track_event_legacy.cc",
         "src/tracing/virtual_destructors.cc",
     ],
 )
@@ -1153,31 +1369,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",
     ],
 )
 
@@ -1189,32 +1385,11 @@
     ],
 )
 
-# GN target: //src/tracing:tracing
+# GN target: //src/tracing:system_backend
 filegroup(
-    name = "src_tracing_tracing",
+    name = "src_tracing_system_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",
     ],
 )
 
@@ -1296,6 +1471,7 @@
         "protos/perfetto/common/observable_events.proto",
         "protos/perfetto/common/sys_stats_counters.proto",
         "protos/perfetto/common/trace_stats.proto",
+        "protos/perfetto/common/tracing_service_capabilities.proto",
         "protos/perfetto/common/tracing_service_state.proto",
         "protos/perfetto/common/track_event_descriptor.proto",
     ],
@@ -1360,6 +1536,7 @@
         ":protos_perfetto_config_process_stats_cpp",
         ":protos_perfetto_config_android_cpp",
         ":protos_perfetto_config_inode_file_cpp",
+        ":protos_perfetto_config_track_event_cpp",
         ":protos_perfetto_config_ftrace_cpp",
         ":protos_perfetto_config_profiling_cpp",
         ":protos_perfetto_config_gpu_cpp",
@@ -1627,6 +1804,7 @@
         ":protos_perfetto_config_process_stats_protos",
         ":protos_perfetto_config_profiling_protos",
         ":protos_perfetto_config_sys_stats_protos",
+        ":protos_perfetto_config_track_event_protos",
     ],
 )
 
@@ -1669,6 +1847,41 @@
     ],
 )
 
+# GN target: //protos/perfetto/config/track_event:cpp
+perfetto_cc_protocpp_library(
+    name = "protos_perfetto_config_track_event_cpp",
+    deps = [
+        ":protos_perfetto_config_track_event_protos",
+    ],
+)
+
+# GN target: //protos/perfetto/config/track_event:lite
+perfetto_cc_proto_library(
+    name = "protos_perfetto_config_track_event_lite",
+    deps = [
+        ":protos_perfetto_config_track_event_protos",
+    ],
+)
+
+# GN target: //protos/perfetto/config/track_event:zero
+perfetto_proto_library(
+    name = "protos_perfetto_config_track_event_protos",
+    srcs = [
+        "protos/perfetto/config/track_event/track_event_config.proto",
+    ],
+    visibility = [
+        PERFETTO_CONFIG.proto_library_visibility,
+    ],
+)
+
+# GN target: //protos/perfetto/config/track_event:zero
+perfetto_cc_protozero_library(
+    name = "protos_perfetto_config_track_event_zero",
+    deps = [
+        ":protos_perfetto_config_track_event_protos",
+    ],
+)
+
 # GN target: //protos/perfetto/config:zero
 perfetto_cc_protozero_library(
     name = "protos_perfetto_config_zero",
@@ -1685,6 +1898,7 @@
         ":protos_perfetto_config_process_stats_cpp",
         ":protos_perfetto_config_android_cpp",
         ":protos_perfetto_config_inode_file_cpp",
+        ":protos_perfetto_config_track_event_cpp",
         ":protos_perfetto_config_ftrace_cpp",
         ":protos_perfetto_config_profiling_cpp",
         ":protos_perfetto_config_gpu_cpp",
@@ -1700,17 +1914,18 @@
     name = "protos_perfetto_ipc_ipc",
     deps = [
         ":protos_perfetto_ipc_protos",
-        ":protos_perfetto_ipc_cpp",
-        ":protos_perfetto_config_process_stats_cpp",
         ":protos_perfetto_config_android_cpp",
-        ":protos_perfetto_config_inode_file_cpp",
-        ":protos_perfetto_config_ftrace_cpp",
-        ":protos_perfetto_config_profiling_cpp",
+        ":protos_perfetto_config_track_event_cpp",
         ":protos_perfetto_ipc_wire_protocol_cpp",
+        ":protos_perfetto_common_cpp",
+        ":protos_perfetto_config_process_stats_cpp",
+        ":protos_perfetto_config_ftrace_cpp",
+        ":protos_perfetto_config_inode_file_cpp",
+        ":protos_perfetto_config_profiling_cpp",
         ":protos_perfetto_config_gpu_cpp",
         ":protos_perfetto_config_cpp",
         ":protos_perfetto_config_power_cpp",
-        ":protos_perfetto_common_cpp",
+        ":protos_perfetto_ipc_cpp",
         ":protos_perfetto_config_sys_stats_cpp",
     ],
 )
@@ -1736,6 +1951,7 @@
         ":protos_perfetto_config_profiling_protos",
         ":protos_perfetto_config_protos",
         ":protos_perfetto_config_sys_stats_protos",
+        ":protos_perfetto_config_track_event_protos",
         ":protos_perfetto_ipc_wire_protocol_protos",
     ],
 )
@@ -1773,9 +1989,11 @@
     srcs = [
         "protos/perfetto/metrics/android/batt_metric.proto",
         "protos/perfetto/metrics/android/cpu_metric.proto",
+        "protos/perfetto/metrics/android/display_metrics.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_histogram.proto",
         "protos/perfetto/metrics/android/java_heap_stats.proto",
         "protos/perfetto/metrics/android/lmk_metric.proto",
         "protos/perfetto/metrics/android/lmk_reason_metric.proto",
@@ -1785,6 +2003,8 @@
         "protos/perfetto/metrics/android/powrails_metric.proto",
         "protos/perfetto/metrics/android/process_metadata.proto",
         "protos/perfetto/metrics/android/startup_metric.proto",
+        "protos/perfetto/metrics/android/task_names.proto",
+        "protos/perfetto/metrics/android/thread_time_in_state_metric.proto",
         "protos/perfetto/metrics/android/unmapped_java_symbols.proto",
         "protos/perfetto/metrics/android/unsymbolized_frames.proto",
     ],
@@ -1841,6 +2061,7 @@
     srcs = [
         "protos/perfetto/trace/android/android_log.proto",
         "protos/perfetto/trace/android/graphics_frame_event.proto",
+        "protos/perfetto/trace/android/initial_display_state.proto",
         "protos/perfetto/trace/android/packages_list.proto",
     ],
     visibility = [
@@ -1942,6 +2163,7 @@
         "protos/perfetto/trace/ftrace/ftrace_stats.proto",
         "protos/perfetto/trace/ftrace/generic.proto",
         "protos/perfetto/trace/ftrace/i2c.proto",
+        "protos/perfetto/trace/ftrace/ion.proto",
         "protos/perfetto/trace/ftrace/ipi.proto",
         "protos/perfetto/trace/ftrace/irq.proto",
         "protos/perfetto/trace/ftrace/kmem.proto",
@@ -2087,6 +2309,7 @@
         ":protos_perfetto_config_profiling_protos",
         ":protos_perfetto_config_protos",
         ":protos_perfetto_config_sys_stats_protos",
+        ":protos_perfetto_config_track_event_protos",
     ],
 )
 
@@ -2127,6 +2350,7 @@
         ":protos_perfetto_config_profiling_protos",
         ":protos_perfetto_config_protos",
         ":protos_perfetto_config_sys_stats_protos",
+        ":protos_perfetto_config_track_event_protos",
         ":protos_perfetto_trace_android_protos",
         ":protos_perfetto_trace_chrome_protos",
         ":protos_perfetto_trace_filesystem_protos",
@@ -2139,6 +2363,7 @@
         ":protos_perfetto_trace_profiling_protos",
         ":protos_perfetto_trace_ps_protos",
         ":protos_perfetto_trace_sys_stats_protos",
+        ":protos_perfetto_trace_system_info_protos",
         ":protos_perfetto_trace_track_event_protos",
     ],
 )
@@ -2164,6 +2389,7 @@
     name = "protos_perfetto_trace_perfetto_protos",
     srcs = [
         "protos/perfetto/trace/perfetto/perfetto_metatrace.proto",
+        "protos/perfetto/trace/perfetto/tracing_service_event.proto",
     ],
     visibility = [
         PERFETTO_CONFIG.proto_library_visibility,
@@ -2234,6 +2460,10 @@
     visibility = [
         PERFETTO_CONFIG.proto_library_visibility,
     ],
+    deps = [
+        ":protos_perfetto_metrics_android_protos",
+        ":protos_perfetto_metrics_protos",
+    ],
 )
 
 # GN target: //protos/perfetto/trace_processor:zero
@@ -2259,6 +2489,7 @@
         "protos/perfetto/trace/profiling/heap_graph.proto",
         "protos/perfetto/trace/profiling/profile_common.proto",
         "protos/perfetto/trace/profiling/profile_packet.proto",
+        "protos/perfetto/trace/profiling/smaps.proto",
     ],
     visibility = [
         PERFETTO_CONFIG.proto_library_visibility,
@@ -2331,6 +2562,33 @@
     ],
 )
 
+# GN target: //protos/perfetto/trace/system_info:lite
+perfetto_cc_proto_library(
+    name = "protos_perfetto_trace_system_info_lite",
+    deps = [
+        ":protos_perfetto_trace_system_info_protos",
+    ],
+)
+
+# GN target: //protos/perfetto/trace/system_info:zero
+perfetto_proto_library(
+    name = "protos_perfetto_trace_system_info_protos",
+    srcs = [
+        "protos/perfetto/trace/system_info/cpu_info.proto",
+    ],
+    visibility = [
+        PERFETTO_CONFIG.proto_library_visibility,
+    ],
+)
+
+# GN target: //protos/perfetto/trace/system_info:zero
+perfetto_cc_protozero_library(
+    name = "protos_perfetto_trace_system_info_zero",
+    deps = [
+        ":protos_perfetto_trace_system_info_protos",
+    ],
+)
+
 # GN target: //protos/perfetto/trace/track_event:lite
 perfetto_cc_proto_library(
     name = "protos_perfetto_trace_track_event_lite",
@@ -2346,10 +2604,12 @@
         "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto",
         "protos/perfetto/trace/track_event/chrome_histogram_sample.proto",
         "protos/perfetto/trace/track_event/chrome_keyed_service.proto",
+        "protos/perfetto/trace/track_event/chrome_latency_info.proto",
         "protos/perfetto/trace/track_event/chrome_legacy_ipc.proto",
         "protos/perfetto/trace/track_event/chrome_process_descriptor.proto",
         "protos/perfetto/trace/track_event/chrome_thread_descriptor.proto",
         "protos/perfetto/trace/track_event/chrome_user_event.proto",
+        "protos/perfetto/trace/track_event/counter_descriptor.proto",
         "protos/perfetto/trace/track_event/debug_annotation.proto",
         "protos/perfetto/trace/track_event/log_message.proto",
         "protos/perfetto/trace/track_event/process_descriptor.proto",
@@ -2420,13 +2680,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_without_backends",
         ":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_backend",
     ],
     hdrs = [
         ":include_perfetto_base_base",
@@ -2463,6 +2731,8 @@
         ":protos_perfetto_config_profiling_zero",
         ":protos_perfetto_config_sys_stats_cpp",
         ":protos_perfetto_config_sys_stats_zero",
+        ":protos_perfetto_config_track_event_cpp",
+        ":protos_perfetto_config_track_event_zero",
         ":protos_perfetto_config_zero",
         ":protos_perfetto_ipc_cpp",
         ":protos_perfetto_ipc_ipc",
@@ -2480,6 +2750,7 @@
         ":protos_perfetto_trace_profiling_zero",
         ":protos_perfetto_trace_ps_zero",
         ":protos_perfetto_trace_sys_stats_zero",
+        ":protos_perfetto_trace_system_info_zero",
         ":protos_perfetto_trace_track_event_zero",
     ],
     linkstatic = True,
@@ -2504,14 +2775,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",
@@ -2536,6 +2810,8 @@
         ":protos_perfetto_config_profiling_zero",
         ":protos_perfetto_config_sys_stats_cpp",
         ":protos_perfetto_config_sys_stats_zero",
+        ":protos_perfetto_config_track_event_cpp",
+        ":protos_perfetto_config_track_event_zero",
         ":protos_perfetto_config_zero",
         ":protos_perfetto_ipc_cpp",
         ":protos_perfetto_ipc_ipc",
@@ -2553,6 +2829,7 @@
         ":protos_perfetto_trace_profiling_zero",
         ":protos_perfetto_trace_ps_zero",
         ":protos_perfetto_trace_sys_stats_zero",
+        ":protos_perfetto_trace_system_info_zero",
         ":protos_perfetto_trace_track_event_zero",
         ":src_perfetto_cmd_protos",
     ] + PERFETTO_CONFIG.deps.zlib,
@@ -2564,17 +2841,23 @@
     srcs = [
         ":src_base_base",
         ":src_protozero_protozero",
+        ":src_trace_processor_analysis_analysis",
         ":src_trace_processor_containers_containers",
         ":src_trace_processor_db_lib",
-        ":src_trace_processor_descriptors",
         ":src_trace_processor_export_json",
+        ":src_trace_processor_ftrace_descriptors",
+        ":src_trace_processor_importers_common",
         ":src_trace_processor_lib",
         ":src_trace_processor_metrics_lib",
         ":src_trace_processor_sqlite_sqlite",
         ":src_trace_processor_storage_full",
         ":src_trace_processor_storage_minimal",
+        ":src_trace_processor_storage_storage",
         ":src_trace_processor_tables_tables",
+        ":src_trace_processor_track_event_descriptor",
         ":src_trace_processor_types_types",
+        ":src_trace_processor_util_descriptors",
+        ":src_trace_processor_util_util",
     ],
     hdrs = [
         ":include_perfetto_base_base",
@@ -2599,6 +2882,7 @@
                ":protos_perfetto_config_process_stats_zero",
                ":protos_perfetto_config_profiling_zero",
                ":protos_perfetto_config_sys_stats_zero",
+               ":protos_perfetto_config_track_event_zero",
                ":protos_perfetto_config_zero",
                ":protos_perfetto_metrics_android_zero",
                ":protos_perfetto_metrics_zero",
@@ -2616,6 +2900,7 @@
                ":protos_perfetto_trace_profiling_zero",
                ":protos_perfetto_trace_ps_zero",
                ":protos_perfetto_trace_sys_stats_zero",
+               ":protos_perfetto_trace_system_info_zero",
                ":protos_perfetto_trace_track_event_zero",
            ] + PERFETTO_CONFIG.deps.jsoncpp +
            PERFETTO_CONFIG.deps.sqlite +
@@ -2630,9 +2915,9 @@
 perfetto_cc_binary(
     name = "trace_processor_shell",
     srcs = [
-        "src/trace_processor/proto_to_json.cc",
-        "src/trace_processor/proto_to_json.h",
         "src/trace_processor/trace_processor_shell.cc",
+        "src/trace_processor/util/proto_to_json.cc",
+        "src/trace_processor/util/proto_to_json.h",
         ":include_perfetto_base_base",
         ":include_perfetto_ext_base_base",
         ":include_perfetto_ext_trace_processor_export_json",
@@ -2646,10 +2931,12 @@
         ":src_profiling_symbolizer_symbolize_database",
         ":src_profiling_symbolizer_symbolizer",
         ":src_protozero_protozero",
+        ":src_trace_processor_analysis_analysis",
         ":src_trace_processor_containers_containers",
         ":src_trace_processor_db_lib",
-        ":src_trace_processor_descriptors",
         ":src_trace_processor_export_json",
+        ":src_trace_processor_ftrace_descriptors",
+        ":src_trace_processor_importers_common",
         ":src_trace_processor_lib",
         ":src_trace_processor_metrics_lib",
         ":src_trace_processor_rpc_httpd",
@@ -2657,8 +2944,12 @@
         ":src_trace_processor_sqlite_sqlite",
         ":src_trace_processor_storage_full",
         ":src_trace_processor_storage_minimal",
+        ":src_trace_processor_storage_storage",
         ":src_trace_processor_tables_tables",
+        ":src_trace_processor_track_event_descriptor",
         ":src_trace_processor_types_types",
+        ":src_trace_processor_util_descriptors",
+        ":src_trace_processor_util_util",
     ],
     visibility = [
         "//visibility:public",
@@ -2673,6 +2964,7 @@
                ":protos_perfetto_config_process_stats_zero",
                ":protos_perfetto_config_profiling_zero",
                ":protos_perfetto_config_sys_stats_zero",
+               ":protos_perfetto_config_track_event_zero",
                ":protos_perfetto_config_zero",
                ":protos_perfetto_metrics_android_zero",
                ":protos_perfetto_metrics_zero",
@@ -2691,6 +2983,7 @@
                ":protos_perfetto_trace_profiling_zero",
                ":protos_perfetto_trace_ps_zero",
                ":protos_perfetto_trace_sys_stats_zero",
+               ":protos_perfetto_trace_system_info_zero",
                ":protos_perfetto_trace_track_event_zero",
            ] + PERFETTO_CONFIG.deps.jsoncpp +
            PERFETTO_CONFIG.deps.linenoise +
@@ -2765,6 +3058,7 @@
         ":protos_perfetto_config_process_stats_zero",
         ":protos_perfetto_config_profiling_zero",
         ":protos_perfetto_config_sys_stats_zero",
+        ":protos_perfetto_config_track_event_zero",
         ":protos_perfetto_config_zero",
         ":protos_perfetto_trace_android_zero",
         ":protos_perfetto_trace_chrome_zero",
@@ -2779,6 +3073,7 @@
         ":protos_perfetto_trace_profiling_zero",
         ":protos_perfetto_trace_ps_zero",
         ":protos_perfetto_trace_sys_stats_zero",
+        ":protos_perfetto_trace_system_info_zero",
         ":protos_perfetto_trace_track_event_zero",
         ":protos_third_party_pprof_zero",
     ] + PERFETTO_CONFIG.deps.zlib,
@@ -2804,17 +3099,23 @@
         ":src_profiling_symbolizer_symbolize_database",
         ":src_profiling_symbolizer_symbolizer",
         ":src_protozero_protozero",
+        ":src_trace_processor_analysis_analysis",
         ":src_trace_processor_containers_containers",
         ":src_trace_processor_db_lib",
-        ":src_trace_processor_descriptors",
         ":src_trace_processor_export_json",
+        ":src_trace_processor_ftrace_descriptors",
+        ":src_trace_processor_importers_common",
         ":src_trace_processor_lib",
         ":src_trace_processor_metrics_lib",
         ":src_trace_processor_sqlite_sqlite",
         ":src_trace_processor_storage_full",
         ":src_trace_processor_storage_minimal",
+        ":src_trace_processor_storage_storage",
         ":src_trace_processor_tables_tables",
+        ":src_trace_processor_track_event_descriptor",
         ":src_trace_processor_types_types",
+        ":src_trace_processor_util_descriptors",
+        ":src_trace_processor_util_util",
         ":tools_trace_to_text_common",
         ":tools_trace_to_text_full",
         ":tools_trace_to_text_pprofbuilder",
@@ -2833,6 +3134,7 @@
                ":protos_perfetto_config_process_stats_zero",
                ":protos_perfetto_config_profiling_zero",
                ":protos_perfetto_config_sys_stats_zero",
+               ":protos_perfetto_config_track_event_zero",
                ":protos_perfetto_config_zero",
                ":protos_perfetto_metrics_android_zero",
                ":protos_perfetto_metrics_zero",
@@ -2850,6 +3152,7 @@
                ":protos_perfetto_trace_profiling_zero",
                ":protos_perfetto_trace_ps_zero",
                ":protos_perfetto_trace_sys_stats_zero",
+               ":protos_perfetto_trace_system_info_zero",
                ":protos_perfetto_trace_track_event_zero",
                ":protos_third_party_pprof_zero",
            ] + PERFETTO_CONFIG.deps.jsoncpp +
diff --git a/BUILD.gn b/BUILD.gn
index 69bbd54..e9e51ec 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -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",
   ]
 }
 
@@ -194,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.
@@ -208,7 +211,6 @@
     complete_static_lib = true
     public_deps = [
       "gn:default_deps",
-      "src/tracing",
       "src/tracing:client_api",
       "src/tracing:platform_posix",
     ]
@@ -227,7 +229,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" ]
@@ -235,6 +241,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",
@@ -243,7 +250,10 @@
       "protos/perfetto/trace/track_event:zero",
     ]
     if (enable_perfetto_ipc) {
-      deps += [ "src/tracing:ipc" ]
+      deps += [
+        "src/tracing/ipc/producer",
+        "src/tracing/ipc/service",
+      ]
       public_deps += [ "include/perfetto/ext/tracing/ipc:ipc" ]
     }
   }
diff --git a/LICENSE b/LICENSE
index 25f8ab9..bb056df 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,16 +1,3 @@
-
-   Copyright (c) 2017, The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-
-   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.
-
-
                                  Apache License
                            Version 2.0, January 2004
                         http://www.apache.org/licenses/
@@ -188,3 +175,15 @@
 
    END OF TERMS AND CONDITIONS
 
+   Copyright (c) 2017, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   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.
+
+
diff --git a/NOTICE b/NOTICE
deleted file mode 100644
index 25f8ab9..0000000
--- a/NOTICE
+++ /dev/null
@@ -1,190 +0,0 @@
-
-   Copyright (c) 2017, The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-
-   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.
-
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
diff --git a/OWNERS b/OWNERS
index 4ed8f1c..3e319bc 100644
--- a/OWNERS
+++ b/OWNERS
@@ -5,12 +5,19 @@
 fmayer@google.com
 taylori@google.com
 rsavitski@google.com
+ilkos@google.com
 
 # For Chrome-related reviews.
 oysteine@google.com
 eseckler@google.com
 nuskos@google.com
+khokhlov@google.com
 ssid@google.com
 
+# chromium.org aliases for DEPS on third_party/perfetto.
+eseckler@chromium.org
+nuskos@chromium.org
+skyostil@chromium.org
+
 # TEAM: tracing@chromium.org
 # COMPONENT: Speed>Tracing
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 1fec1b0..30eb1c1 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -44,7 +44,7 @@
   results += CheckBinaryDescriptors(input, output)
   results += CheckMergedTraceConfigProto(input, output)
   results += CheckWhitelist(input, output)
-  results += CheckBadCpp(input, output)
+  results += CheckBannedCpp(input, output)
   return results
 
 
@@ -109,9 +109,7 @@
   return []
 
 
-def CheckBadCpp(input_api, output_api):
-  tool = 'tools/check_include_violations'
-
+def CheckBannedCpp(input_api, output_api):
   bad_cpp = [
       (r'\bNULL\b', 'New code should not use NULL prefer nullptr'),
       (r'\bstd::stoi\b',
@@ -130,6 +128,9 @@
        'std::stod throws exceptions prefer base::StringToDouble()'),
       (r'\bstd::stold\b',
        'std::stold throws exceptions prefer base::StringToDouble()'),
+      (r'\bPERFETTO_EINTR\(close\(',
+       'close(2) must not be retried on EINTR on Linux and other OSes '
+       'that we run on, as the fd will be closed.'),
   ]
 
   def file_filter(x):
@@ -141,7 +142,7 @@
       for regex, message in bad_cpp:
         if input_api.re.search(regex, line):
           errors.append(
-              output_api.PresubmitError('Banned C++:\n  {}:{} {}'.format(
+              output_api.PresubmitError('Banned pattern:\n  {}:{} {}'.format(
                   f.LocalPath(), line_number, message)))
   return errors
 
diff --git a/README.chromium b/README.chromium
index 33541c5..25ec614 100644
--- a/README.chromium
+++ b/README.chromium
@@ -2,7 +2,7 @@
 URL: https://android.googlesource.com/platform/external/perfetto/
 Version: unknown
 License: Apache2
-License File: NOTICE
+License File: LICENSE
 Security Critical: yes
 License Android Compatible: yes
 Description: Performance instrumentation and logging for Google client platforms
diff --git a/TEST_MAPPING b/TEST_MAPPING
index de552aa..4c2f19d 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -2,6 +2,16 @@
   "presubmit": [
     {
       "name": "CtsPerfettoTestCases"
+    },
+    // Additional presubmit tests that explicitly exercise
+    // Perfetto's backend
+    {
+      "name": "libsurfaceflinger_unittest",
+      "options": [
+          {
+	      "include-filter": "FrameTracerTest.*"
+	  }
+      ]
     }
   ]
 }
diff --git a/buildtools/BUILD.gn b/buildtools/BUILD.gn
index f8d73c9..5195b1b 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,37 +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",
-  ]
-}
-
-# This config is applied to the autogenerated .pb.{cc,h} files in
-# //gn/standalone/proto_library.gni. This config is propagated up to the source sets
-# that depend on generated proto headers. Therefore this should stay as lean and
-# clean as possible in terms of -W-no* suppressions. Thankfully the
-# autogenerated .pb.h headers violate less warnings than the libprotobuf_*
-# library itself.
-config("protobuf_gen_config") {
-  visibility = [ "*" ]  # This is injected by standalone/proto_library.gni
-  defines = [
-    "GOOGLE_PROTOBUF_NO_RTTI",
-    "GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
-  ]
-  cflags = [
-    # Using -isystem instead of include_dirs (-I), so we don't need to suppress
-    # warnings coming from libprotobuf headers. Doing so would mask warnings in
-    # our own code.
-    "-isystem",
-    rebase_path("protobuf/src", root_build_dir),
-    "-Wno-unknown-warning-option",
-    "-Wno-deprecated",
-    "-Wno-undef",
-    "-Wno-zero-as-null-pointer-constant",
-  ]
+  sources = [ "googletest/googlemock/src/gmock-all.cc" ]
+  deps = [ "//gn:default_deps" ]
 }
 
 # Configuration used to build libprotobuf_* and the protoc compiler.
@@ -162,7 +125,7 @@
   visibility = _buildtools_visibility
 
   # Apply the lighter supressions and macro definitions from above.
-  configs = [ ":protobuf_gen_config" ]
+  configs = [ "//gn:protobuf_gen_config" ]
 
   defines = [ "HAVE_PTHREAD=1" ]
   if (is_clang) {
@@ -256,10 +219,8 @@
   ]
   configs -= [ "//gn/standalone:extra_warnings" ]
   configs += [ ":protobuf_config" ]
-  public_configs = [ ":protobuf_gen_config" ]
-  deps = [
-    "//gn:default_deps",
-  ]
+  public_configs = [ "//gn:protobuf_gen_config" ]
+  deps = [ "//gn:default_deps" ]
 }
 
 source_set("protobuf_full") {
@@ -386,7 +347,7 @@
   ]
   configs -= [ "//gn/standalone:extra_warnings" ]
   configs += [ ":protobuf_config" ]
-  public_configs = [ ":protobuf_gen_config" ]
+  public_configs = [ "//gn:protobuf_gen_config" ]
 }
 
 source_set("protoc_lib") {
@@ -565,7 +526,7 @@
   ]
   configs -= [ "//gn/standalone:extra_warnings" ]
   configs += [ ":protobuf_config" ]
-  public_configs = [ ":protobuf_gen_config" ]
+  public_configs = [ "//gn:protobuf_gen_config" ]
 }
 
 if (current_toolchain == host_toolchain) {
@@ -575,9 +536,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 +620,7 @@
       ":libc++config",
       "//gn/standalone/sanitizers:sanitizer_options_link_helper",
     ]
-    deps = [
-      ":libunwind",
-    ]
+    deps = [ ":libunwind" ]
   }
 
   if (custom_libcxx_is_static) {
@@ -679,8 +636,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 +650,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 +678,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 +739,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 +773,7 @@
     ]
     configs -= [ "//gn/standalone:extra_warnings" ]
     public_configs = [ ":libbacktrace_config" ]
-    deps = [
-      "//gn:default_deps",
-    ]
+    deps = [ "//gn:default_deps" ]
   }
 }
 
@@ -834,6 +795,8 @@
     "-DSQLITE_TEMP_STORE=3",
     "-DSQLITE_OMIT_LOAD_EXTENSION",
     "-DSQLITE_OMIT_RANDOMNESS",
+    "-DSQLITE_OMIT_AUTOINIT",
+    "-DSQLITE_ENABLE_JSON1",
   ]
 }
 
@@ -847,17 +810,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 +871,7 @@
     "-Wno-empty-body",
     "-Wno-enum-conversion",
   ]
-  deps = [
-    "//gn:default_deps",
-  ]
+  deps = [ "//gn:default_deps" ]
 }
 
 source_set("zlib") {
@@ -939,9 +896,7 @@
   configs -= [ "//gn/standalone:extra_warnings" ]
   cflags = []
   public_configs = [ ":zlib_config" ]
-  deps = [
-    "//gn:default_deps",
-  ]
+  deps = [ "//gn:default_deps" ]
 }
 
 config("zlib_config") {
@@ -973,10 +928,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/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",
@@ -1002,7 +960,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" ]
@@ -1014,10 +971,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 = [
@@ -1040,9 +1006,7 @@
   ]
   configs -= [ "//gn/standalone:extra_warnings" ]
   public_configs = [ ":jsoncpp_config" ]
-  deps = [
-    "//gn:default_deps",
-  ]
+  deps = [ "//gn:default_deps" ]
 }
 
 config("linenoise_config") {
@@ -1065,9 +1029,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) {
@@ -1102,8 +1064,6 @@
       "libfuzzer/FuzzerUtilPosix.cpp",
       "libfuzzer/FuzzerUtilWindows.cpp",
     ]
-    deps = [
-      "//gn:default_deps",
-    ]
+    deps = [ "//gn:default_deps" ]
   }
 }
diff --git a/debian/rules b/debian/rules
index 1a92a13..594466f 100755
--- a/debian/rules
+++ b/debian/rules
@@ -3,7 +3,7 @@
 	dh $@
 
 override_dh_auto_configure:
-	tools/install-build-deps --no-android
+	tools/install-build-deps
 	tools/gn gen out/release --args="is_debug=false"
 
 override_dh_auto_build:
diff --git a/docs/analysis.md b/docs/analysis.md
new file mode 100644
index 0000000..406fc99
--- /dev/null
+++ b/docs/analysis.md
@@ -0,0 +1,194 @@
+# Trace analysis
+
+Trace analysis refers to a set of features built into the Perfetto
+[trace processor](trace-processor.md) and Perfetto UI which enrich a trace with
+extra information synthesized during the import of a trace. There are three
+features which currently part of this project:
+
+- [Adding descriptions of slices](#descriptions)
+- [Annotating the trace with new events](#annotations)
+- [Alerts](#alerts)
+
+## <a name="descriptions"></a>Adding descriptions to slices
+
+![Descriptions in action](images/description.png "Descriptions")
+**<p align="center">Description for the measure slice</p>**
+
+### Background
+
+Descriptions attach a human-readable description to a slice in the trace. This
+can include information like the source of a slice, why a slice is important and
+links to documentation where the viewer can learn more about the slice. In
+essence, descriptions act as if an expert was telling the user what the slice
+means.
+
+For example, consider the `inflate` slice which occurs during view inflation in
+Android. We can add the following description and link:
+
+```
+Description: Constructing a View hierarchy from pre-processed XML via LayoutInflater#layout. This includes constructing all of the View objects in the hierarchy, and applying styled attributes.
+
+Link: https://developer.android.com/reference/android/view/layoutinflater#inflate(int,%20android.view.viewgroup)
+```
+
+### Adding descriptions to a slice
+
+Adding a new event just requires a self-contained change to the
+[`DescribeSlice`](../src/trace_processor/analysis/describe_slice.h) function.
+The inputs are the table containing all the slices from the trace and the id of
+the slice which an embedder (e.g. the UI) is requesting a description for. The
+output is a `SliceDescription` which is simply a `pair<description, doc link>`.
+
+Currently, all implemented descriptions are based on only the name of the slice
+itself. However, it is straightforward to extend this to also consider the
+ancestor slices and other similar properties of the slice and we plan on doing
+this in the future.
+
+### Using descriptions as a trace processor embedder
+
+The `DescribeSlice` function is exposed to SQL through the `describe_slice`
+table. This table has the following schema:
+
+| Name        | Type   | Meaning                                                                      |
+| :---------- | ------ | ---------------------------------------------------------------------------- |
+| description | string | Provides the description for the given slice                                 |
+| doc_link    | string | Provides a hyperlink to documentation which gives more context for the slice |
+
+The table also has a hidden column `slice_id` which need to be set equal to the
+id of the slice that you want to obtain the description from. For example, to
+get the description and doc link for slice with id `5`:
+
+```sqlite
+select description, doc_link
+from describe_slice
+where slice_id = 5
+```
+
+You can also _join_ the `describe_slice` table with the slice table to obtain
+descriptions for more than one slice. For example, to get the ts, duration and
+description for all `measure` slices:
+
+```sqlite
+select ts, dur, description
+from slice s
+join desribe_slice d on s.id = d.slice_id
+where name = 'measure'
+```
+
+## <a name="annotations"></a>Annotating the trace with new events
+
+![Slice annotations](images/annotation-slice.png "Slice annotations")
+**<p align="center">Annotation slice track containing app startups</p>**
+
+![Counter annotations](images/annotation-counter.png "Counter annotations")
+**<p align="center">Annotation counter track added to measure ION
+allocations</p>**
+
+### Background
+
+The annotations feature allows creation of new events (slices and counters) from
+the data in the trace. These events can then be displayed in the UI tracks as if
+they were part of the trace itself.
+
+This feature is useful as often the data in the trace is very low-level. While
+this low level information is important to expose for experts to perform deep
+debugging, often the user is looking to get a high level overview without
+needing to piece together events from multiple places in the trace.
+
+For example, an app startup in Android spans multiple components including
+`ActivityManager`, `system_server` and the newly created app process derived
+from `zygote`. Most users do not need startup broken down to this level of
+detail; instead they are simply interested in a single slice spanning the whole
+startup duration.
+
+The annotations feature is tied very closely to [metrics subsystem](metrics.md);
+Often the SQL-based metrics often need to create higher-level abstractions from
+raw slices as intermediate artifacts. From previous example, the
+[startup metric](../src/trace_processor/metrics/android/android_startup.sql), it
+creates the exact `launching` slice we want to display in the UI.
+
+The other benefit of aligning the two is that changes in metrics are
+automatically kept in sync with what the user sees in the UI.
+
+### Adding annotations to a new or existing metric
+
+As annotations depend on metrics, the initial steps are same as that of
+[developing a metric](metrics.md). In summary:
+
+- Create a new proto message for your metric and add it to the
+  [`TraceMetrics`](../protos/perfetto/metrics/metrics.proto) proto
+- Write a new SQL metric file in the [metrics](../src/trace_processor/metrics)
+  folder. Good examples to follow are
+  [ion](../src/trace_processor/metrics/android/android_ion.sql) and
+  [startup](../src/trace_processor/metrics/android/android_startup.sql) metrics
+
+**Note**: the metric can be just an empty proto message during prototyping or if
+you think that no summarisation is necessary. However, generally if an event is
+important enough to display in the UI, it should also be tracked in benchmarks
+as a metric.
+
+To extend a metric with annotations, a new table or view with the name
+`<metric name>_annotations` needs to be created (the trailing `_annotations`
+suffix in the table name is important). For example, for the
+[`android_startup`]() metric, we create a view named
+`android_startup_annotations`.
+
+The schema of this table/view is as follows:
+
+| Name         | Type     | Presence                              | Meaning                                       |
+| :----------- | -------- | ------------------------------------- | --------------------------------------------- |
+| `track_type` | `string` | Mandatory                             | 'slice' for slices, 'counter' for counters    |
+| `track_name` | `string` | Mandatory                             | Name of the track to display in the UI        |
+| `ts`         | `int64`  | Mandatory                             | The timestamp of the event (slice or counter) |
+| `dur`        | `int64`  | Mandatory for slice, NULL for counter | The duration of the slice                     |
+| `slice_name` | `string` | Mandatory for slice, NULL for counter | The name of the slice                         |
+| `value`      | `double` | Mandatory for counter, NULL for slice | The value of the counter                      |
+
+**Note:** `track_name` acts as the track identifier i.e. all events with the
+same `track_name` are placed onto the same track.
+
+Currently, there are a few limitations to what can be displayed with
+annotations:
+
+- Nested slices within the same track are not supported. We plan to support this
+  once we have a concrete usecase.
+- Tracks are always created in the global scope. We plan to extend this to
+  threads and processes in the near future with additional contexts added as
+  necessary.
+- Instant events are currently not supported in the UI but this will be
+  implemented in the near future. In trace processor, instants are always `0`
+  duration slices with special rendering on the UI side.
+- There is no way to tie newly added events back to the source events in the
+  trace which were used to generate them. This is not currently a priority but
+  something we may add in the future.
+
+### Using annotations as a trace processor embedder
+
+As annotations are tied to the metrics subsystem, the `ComputeMetrics` function
+in the trace processor API should be called with the appropriate metrics. This
+will create the `<metric_name>_annotations` table/view which can then be queried
+using the `ExectueQuery` function.
+
+**Note**: We plan at some point to have an API which does not create and return
+the full metrics proto but instead just executes the queries in the metric.
+
+## <a name="alerts"></a>Alerts
+
+### Background
+
+Alerts are used to draw the attention of the user to interesting parts of the
+trace; this are usually warnings or errors about anomalies which occured in the
+trace.
+
+### Current status
+
+Currently, alerts are not implemented in the trace processor but the annotations
+feature was designed with them in mind. We plan on adding another column
+`alert_type` (name to be finalized) to the annotations table which can have the
+value `warning`, `error` or `null`. Depending on this value, the Perfetto UI
+will flag these events to the user.
+
+**Note**: we do not plan on supporting case where alerts need to be added to
+existing events. Instead, new events should be created using annotations and
+alerts added on these instead; this is because the trace processor storage is
+append-only.
diff --git a/docs/app-instrumentation.md b/docs/app-instrumentation.md
new file mode 100644
index 0000000..697ee49
--- /dev/null
+++ b/docs/app-instrumentation.md
@@ -0,0 +1,566 @@
+# App instrumentation
+
+The Perfetto Client API is a C++ library that allows applications to emit
+trace events to add more context to a Perfetto trace to help with
+development, debugging and performance analysis.
+
+> The code from this example is also available as a [GitHub repository](
+> https://github.com/skyostil/perfetto-sdk-example).
+
+To start using the Client API, first check out the latest SDK release:
+
+```sh
+$ git clone https://android.googlesource.com/platform/external/perfetto -b latest
+```
+
+The SDK consists of two files, `sdk/perfetto.h` and
+`sdk/perfetto.cc`. These are an amalgamation of the Client API designed to
+easy to integrate to existing build systems. For example, to add the SDK to a
+CMake project, edit your `CMakeLists.txt` accordingly:
+
+```cmake
+cmake_minimum_required(VERSION 3.13)
+project(PerfettoExample)
+find_package(Threads)
+
+# Define a static library for Perfetto.
+include_directories(perfetto/sdk)
+add_library(perfetto STATIC perfetto/sdk/perfetto.cc)
+
+# Link the library to your main executable.
+add_executable(example example.cc)
+target_link_libraries(example perfetto ${CMAKE_THREAD_LIBS_INIT})
+```
+
+Next, initialize Perfetto in your program:
+
+```C++
+
+#include <perfetto.h>
+
+int main(int argv, char** argc) {
+  perfetto::TracingInitArgs args;
+
+  // The backends determine where trace events are recorded. You may select one
+  // or more of:
+
+  // 1) The in-process backend only records within the app itself.
+  args.backends |= perfetto::kInProcessBackend;
+
+  // 2) The system backend writes events into a system Perfetto daemon,
+  //    allowing merging app and system events (e.g., ftrace) on the same
+  //    timeline. Requires the Perfetto `traced` daemon to be running (e.g.,
+  //    on Android Pie and newer).
+  args.backends |= perfetto::kSystemBackend;
+
+  perfetto::Tracing::Initialize(args);
+}
+```
+
+You are now ready to instrument your app with trace events. The Client API
+has two options for this:
+
+- [Track events](#track-events), which represent time-bounded operations
+   (e.g., function calls) on a timeline. Track events are a good choice for
+   most apps.
+
+- [Custom data sources](#custom-data-sources), which can be used to
+   efficiently record arbitrary app-defined data using a protobuf encoding.
+   Custom data sources are a typically better match for advanced Perfetto
+   users.
+
+# Track events
+
+![Track events shown in the Perfetto UI](
+  track-events.png "Track events in the Perfetto UI")
+
+*Track events* are application specific, time bounded events recorded into a
+*trace* while the application is running. Track events are always associated
+with a *track*, which is a timeline of monotonically increasing time. A track
+corresponds to an independent sequence of execution, such as a single thread
+in a process.
+
+There are a few main types of track events:
+
+1. **Slices**, which represent nested, time bounded operations. For example,
+  a slice could cover the time period from when a function begins executing
+  to when it returns, the time spent loading a file from the network or the
+  time spent blocked on a disk read.
+
+2. **Counters**, which are snapshots of time-varying numeric values. For
+  example, a track event can record instantaneous the memory usage of a
+  process during its execution.
+
+3. **Flows**, which are used to connect related slices that span different
+  tracks together. For example, if an image file is first loaded from
+  the network and then decoded on a thread pool, a flow event can be used to
+  highlight its path through the system. (Not fully implemented yet).
+
+The [Perfetto UI](https://ui.perfetto.dev) has built in support for track
+events, which provides a useful way to quickly visualize the internal
+processing of an app. For example, the [Chrome
+browser](https://www.chromium.org/developers/how-tos/trace-event-profiling-tool)
+is deeply instrumented with track events to assist in debugging, development
+and performance analysis.
+
+A typical use case for track events is annotating a function with a scoped
+track event, so that function's execution shows up in a trace. To start using
+track events, first define the set of categories that your events will fall
+into. Each category can be separately enabled or disabled for tracing (see
+[Category configuration](#category-configuration).
+
+Add the list of categories into a header file (e.g., `example_tracing.h`)
+like this:
+
+```C++
+#include <perfetto.h>
+
+PERFETTO_DEFINE_CATEGORIES(
+    perfetto::Category("rendering")
+        .SetDescription("Events from the graphics subsystem"),
+    perfetto::Category("network")
+        .SetDescription("Network upload and download statistics"));
+```
+
+Then, declare static storage for the categories in a cc file (e.g.,
+`example_tracing.cc`):
+
+```C++
+#include "example_tracing.h"
+
+PERFETTO_TRACK_EVENT_STATIC_STORAGE();
+```
+
+Finally, initialize track events after the client library is brought up:
+
+```C++
+int main(int argv, char** argc) {
+  ...
+  perfetto::Tracing::Initialize(args);
+  perfetto::TrackEvent::Register();  // Add this.
+}
+```
+
+Now you can add track events to existing functions like this:
+
+```C++
+#include "example_tracing.h"
+
+void DrawPlayer() {
+  TRACE_EVENT("rendering", "DrawPlayer");
+  ...
+}
+```
+
+This type of trace event is scoped, which means it will cover the time from
+when the function began executing until the point of return. You can also
+supply (up to two) debug annotations together with the event.
+
+```C++
+int player_number = 1;
+TRACE_EVENT("rendering", "DrawPlayer", "player_number", player_number);
+```
+
+For more complex arguments, you can define [your own protobuf
+messages](../protos/perfetto/trace/track_event/track_event.proto) and emit
+them as a parameter for the event.
+
+> Currently custom protobuf messages need to be added directly to the
+> Perfetto repository under `protos/perfetto/trace`, and Perfetto itself must
+> also be rebuilt. We are working [to lift this
+> limitation](https://github.com/google/perfetto/issues/11).
+
+As an example of a custom track event argument type, save the following as
+`protos/perfetto/trace/track_event/player_info.proto`:
+
+```protobuf
+message PlayerInfo {
+  optional string name = 1;
+  optional uint64 score = 2;
+}
+```
+
+This new file should also be added to
+`protos/perfetto/trace/track_event/BUILD.gn`:
+
+```json
+sources = [
+  ...
+  "player_info.proto"
+]
+```
+
+Also, a matching argument should be added to the track event message
+definition in
+`protos/perfetto/trace/track_event/track_event.proto`:
+
+```protobuf
+import "protos/perfetto/trace/track_event/player_info.proto";
+
+...
+
+message TrackEvent {
+  ...
+  // New argument types go here :)
+  optional PlayerInfo player_info = 1000;
+}
+```
+
+The corresponding trace point could look like this:
+
+```C++
+Player my_player;
+TRACE_EVENT("category", "MyEvent", [&](perfetto::EventContext ctx) {
+  auto player = ctx.event()->set_player_info();
+  player->set_name(my_player.name());
+  player->set_player_score(my_player.score());
+});
+```
+
+The lambda function passed to the macro is only called if tracing is enabled for
+the given category. It is always called synchronously and possibly multiple
+times if multiple concurrent tracing sessions are active.
+
+Now that you have instrumented your app with track events, you are ready to
+start [recording traces](recording-traces.md).
+
+## Category configuration
+
+All track events are assigned to one more trace categories. For example:
+
+```C++
+TRACE_EVENT("rendering", ...);  // Event in the "rendering" category.
+```
+
+By default, all non-debug and non-slow track event categories are enabled for
+tracing. *Debug* and *slow* categories are categories with special tags:
+
+  - `"debug"` categories can give more verbose debugging output for a particular
+    subsystem.
+  - `"slow"` categories record enough data that they can affect the interactive
+    performance of your app.
+
+Category tags can be can be defined like this:
+
+```C++
+perfetto::Category("rendering.debug")
+    .SetDescription("Debug events from the graphics subsystem")
+    .SetTags("debug", "my_custom_tag")
+```
+
+A single trace event can also belong to multiple categories:
+
+```C++
+// Event in the "rendering" and "benchmark" categories.
+TRACE_EVENT("rendering,benchmark", ...);
+```
+
+A corresponding category group entry must be added to the category registry:
+
+```C++
+perfetto::Category::Group("rendering,benchmark")
+```
+
+It's also possible to efficiently query whether a given category is enabled
+for tracing:
+
+```C++
+if (TRACE_EVENT_CATEGORY_ENABLED("rendering")) {
+  // ...
+}
+```
+
+The `TrackEventConfig` field in Perfetto's `TraceConfig` can be used to
+select which categories are enabled for tracing:
+
+```protobuf
+message TrackEventConfig {
+  // Each list item is a glob. Each category is matched against the lists
+  // as explained below.
+  repeated string disabled_categories = 1;  // Default: []
+  repeated string enabled_categories = 2;   // Default: []
+  repeated string disabled_tags = 3;        // Default: [“slow”, “debug”]
+  repeated string enabled_tags = 4;         // Default: []
+}
+```
+
+To determine if a category is enabled, it is checked against the filters in the
+following order:
+
+1. Exact matches in enabled categories.
+2. Exact matches in enabled tags.
+3. Exact matches in disabled categories.
+4. Exact matches in disabled tags.
+5. Pattern matches in enabled categories.
+6. Pattern matches in enabled tags.
+7. Pattern matches in disabled categories.
+8. Pattern matches in disabled tags.
+
+If none of the steps produced a match, the category is enabled by default. In
+other words, every category is implicitly enabled unless specifically disabled.
+For example:
+
+| Setting                         | Needed configuration                         |
+| ------------------------------- | -------------------------------------------- |
+| Enable just specific categories | `enabled_categories = [“foo”, “bar”, “baz”]` |
+|                                 | `disabled_categories = [“*”]`                |
+| Enable all non-slow categories  | (Happens by default.)                        |
+| Enable specific tags            | `disabled_tags = [“*”]`                      |
+|                                 | `enabled_tags = [“foo”, “bar”]`              |
+
+## Dynamic and test-only categories
+
+Ideally all trace categories should be defined at compile time as shown
+above, as this ensures trace points will have minimal runtime and binary size
+overhead. However, in some cases trace categories can only be determined at
+runtime (e.g., by JavaScript). These can be used by trace points as follows:
+
+```C++
+perfetto::DynamicCategory dynamic_category{"nodejs.something"};
+TRACE_EVENT(dynamic_category, "SomeEvent", ...);
+```
+
+> Tip: It's also possible to use dynamic event names by passing `nullptr` as
+> the name and filling in the `TrackEvent::name` field manually.
+
+Some trace categories are only useful for testing, and they should not make
+it into a production binary. These types of categories can be defined with a
+list of prefix strings:
+
+```C++
+PERFETTO_DEFINE_TEST_CATEGORY_PREFIXES(
+   "test",
+   "cat"
+);
+```
+
+# Custom data sources
+
+For most uses, track events are the most straightforward way of instrumenting
+your app for tracing. However, in some rare circumstances they are not
+flexible enough, e.g., when the data doesn't fit the notion of a track or is
+high volume enough that it need strongly typed schema to minimize the size of
+each event. In this case, you can implement a *custom data source* for
+Perfetto.
+
+Note that when working with custom data sources, you will also need
+corresponding changes in [trace processor](trace-processor.md) to enable
+importing your data format.
+
+A custom data source is a subclass of `perfetto::DataSource`. Perfetto with
+automatically create one instance of the class for each tracing session it is
+active in (usually just one).
+
+```C++
+class CustomDataSource : public perfetto::DataSource<CustomDataSource> {
+ public:
+  void OnSetup(const SetupArgs&) override {
+    // Use this callback to apply any custom configuration to your data source
+    // based on the TraceConfig in SetupArgs.
+  }
+
+  void OnStart(const StartArgs&) override {
+    // This notification can be used to initialize the GPU driver, enable
+    // counters, etc. StartArgs will contains the DataSourceDescriptor,
+    // which can be extended.
+  }
+
+  void OnStop(const StopArgs&) override {
+    // Undo any initialization done in OnStart.
+  }
+
+  // Data sources can also have per-instance state.
+  int my_custom_state = 0;
+};
+
+PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS(CustomDataSource);
+```
+
+The data source's static data should be defined in one source file like this:
+
+```C++
+PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(CustomDataSource);
+```
+
+Custom data sources need to be registered with Perfetto:
+
+```C++
+int main(int argv, char** argc) {
+  ...
+  perfetto::Tracing::Initialize(args);
+  // Add the following:
+  perfetto::DataSourceDescriptor dsd;
+  dsd.set_name("com.example.custom_data_source");
+  CustomDataSource::Register(dsd);
+}
+```
+
+As with all data sources, the custom data source needs to be specified in the
+trace config to enable tracing:
+
+```C++
+perfetto::TraceConfig cfg;
+auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+ds_cfg->set_name("com.example.custom_data_source");
+```
+
+Finally, call the `Trace()` method to record an event with your custom data
+source. The lambda function passed to that method will only be called if tracing
+is enabled. It is always called synchronously and possibly multiple times if
+multiple concurrent tracing sessions are active.
+
+```C++
+CustomDataSource::Trace([](CustomDataSource::TraceContext ctx) {
+  auto packet = ctx.NewTracePacket();
+  packet->set_timestamp(perfetto::TrackEvent::GetTraceTimeNs());
+  packet->set_for_testing()->set_str("Hello world!");
+});
+```
+
+If necessary the `Trace()` method can access the custom data source state
+(`my_custom_state` in the example above). Doing so, will take a mutex to
+ensure data source isn't destroyed (e.g., because of stopping tracing) while
+the `Trace()` method is called on another thread. For example:
+
+```C++
+CustomDataSource::Trace([](CustomDataSource::TraceContext ctx) {
+  auto safe_handle = trace_args.GetDataSourceLocked();  // Holds a RAII lock.
+  DoSomethingWith(safe_handle->my_custom_state);
+});
+```
+
+# Performance
+
+Perfetto's trace points are designed to have minimal overhead when tracing is
+disabled while providing high throughput for data intensive tracing use
+cases. While exact timings will depend on your system, there is a
+[microbenchmark](../src/tracing/api_benchmark.cc) which gives some ballpark
+figures:
+
+| Scenario | Runtime on Pixel 3 XL | Runtime on ThinkStation P920 |
+| -------- | --------------------- | ---------------------------- |
+| `TRACE_EVENT(...)` (disabled)              | 2 ns   | 1 ns   |
+| `TRACE_EVENT("cat", "name")`               | 285 ns | 630 ns |
+| `TRACE_EVENT("cat", "name", <lambda>)`     | 304 ns | 663 ns |
+| `TRACE_EVENT("cat", "name", "key", value)` | 354 ns | 664 ns |
+| `DataSource::Trace(<lambda>)` (disabled)   | 2 ns   | 1 ns   |
+| `DataSource::Trace(<lambda>)`              | 133 ns | 58 ns  |
+
+# Advanced topics
+
+## Tracks
+
+Every track event is associated with a track, which specifies the timeline
+the event belongs to. In most cases, a track corresponds to a visual
+horizontal track in the Perfetto UI like this:
+
+![Track timelines shown in the Perfetto UI](
+  track-timeline.png "Track timelines in the Perfetto UI")
+
+Events that describe parallel sequences (e.g., separate
+threads) should use separate tracks, while sequential events (e.g., nested
+function calls) generally belong on the same track.
+
+Perfetto supports three kinds of tracks:
+
+1. `Track` – a basic timeline.
+
+2. `ProcessTrack` – a timeline that represents a single process in the system.
+
+3. `ThreadTrack` – a timeline that represents a single thread in the system.
+
+Tracks can have a parent track, which is used to group related tracks
+together. For example, the parent of a `ThreadTrack` is the `ProcessTrack` of
+the process the thread belongs to. By default, tracks are grouped under the
+current process's `ProcessTrack`.
+
+A track is identified by a uuid, which must be unique across the entire
+recorded trace. To minimize the chances of accidental collisions, the uuids
+of child tracks are combined with those of their parents, with each
+`ProcessTrack` having a random, per-process uuid.
+
+By default, track events (e.g., `TRACE_EVENT`) use the `ThreadTrack` for the
+calling thread. This can be overridden, for example, to mark events that
+begin and end on a different thread:
+
+```C++
+void OnNewRequest(size_t request_id) {
+  // Open a slice when the request came in.
+  TRACE_EVENT_BEGIN("category", "HandleRequest", perfetto::Track(request_id));
+
+  // Start a thread to handle the request.
+  std::thread worker_thread([=] {
+    // ... produce response ...
+
+    // Close the slice for the request now that we finished handling it.
+    TRACE_EVENT_END("category", perfetto::Track(request_id));
+  });
+```
+Tracks can also optionally be annotated with metadata:
+
+```C++
+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:
+
+```C++
+perfetto::TrackEvent::EraseTrackDescriptor(track);
+```
+
+## Interning
+
+Interning can be used to avoid repeating the same constant data (e.g., event
+names) throughout the trace. Perfetto automatically performs interning for
+most strings passed to `TRACE_EVENT`, but it's also possible to also define
+your own types of interned data.
+
+First, define an interning index for your type. It should map to a specific
+field of
+[interned_data.proto](../protos/perfetto/trace/interned_data/interned_data.proto)
+and specify how the interned data is written into that message when seen for
+the first time.
+
+```C++
+struct MyInternedData
+    : public perfetto::TrackEventInternedDataIndex<
+        MyInternedData,
+        perfetto::protos::pbzero::InternedData::kMyInternedDataFieldNumber,
+        const char*> {
+  static void Add(perfetto::protos::pbzero::InternedData* interned_data,
+                   size_t iid,
+                   const char* value) {
+    auto my_data = interned_data->add_my_interned_data();
+    my_data->set_iid(iid);
+    my_data->set_value(value);
+  }
+};
+```
+
+Next, use your interned data in a trace point as shown below. The interned
+string will only be emitted the first time the trace point is hit (unless the
+trace buffer has wrapped around).
+
+```C++
+TRACE_EVENT(
+   "category", "Event", [&](perfetto::EventContext ctx) {
+     auto my_message = ctx.event()->set_my_message();
+     size_t iid = MyInternedData::Get(&ctx, "Repeated data to be interned");
+     my_message->set_iid(iid);
+   });
+```
+
+Note that interned data is strongly typed, i.e., each class of interned data
+uses a separate namespace for identifiers.
+
+## Counters
+
+TODO(skyostil).
+
+## Flow events
+
+TODO(skyostil).
diff --git a/docs/architecture.md b/docs/architecture.md
index 7bc5a28..7d57bad 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -111,7 +111,7 @@
   and without repetitions (see [trace-format.md](trace-format.md) for more).
 
 See the comments in
-[shared_memory_abi.h](/include/perfetto/tracing/core/shared_memory_abi.h)
+[shared_memory_abi.h](/include/perfetto/ext/tracing/core/shared_memory_abi.h)
 for more details about the binary format of this buffer.
 
 Other resources
diff --git a/docs/build-instructions.md b/docs/build-instructions.md
index f2bfe35..a68cb46 100644
--- a/docs/build-instructions.md
+++ b/docs/build-instructions.md
@@ -24,7 +24,7 @@
 **Standalone checkout**:  
 All dependent libraries are self-hosted and pulled through:
 ```
-$ tools/install-build-deps [--no-android] [--ui]
+$ tools/install-build-deps [--android] [--ui]
 ```
 
 **Android tree**:  
@@ -67,6 +67,20 @@
 Executables and shared libraries are stripped by default by the Android build
 system. The unstripped artifacts are kept into `out/target/product/XXX/symbols`.
 
+IDE setup
+---------
+
+Use a following command in the checkout directory in order to generate the
+compilation database file:
+
+```
+$ tools/ninja -C out/android -t compdb cc cxx > compile_commands.json
+```
+
+After generating, it can be used in CLion (File -> Open -> Open As Project),
+Visual Studio Code with C/C++ extension and any other tool and editor that
+supports the compilation database format.
+
 Build files
 -----------
 The source of truth of our build file is in the BUILD.gn files, which are based on [GN][gn-quickstart].
diff --git a/docs/clock-sync.md b/docs/clock-sync.md
index 160f505..7ce7894 100644
--- a/docs/clock-sync.md
+++ b/docs/clock-sync.md
@@ -37,7 +37,7 @@
 
 ### 1. The [`timestamp_clock_id`][timestamp_clock_id] field of TracePacket
 
-```proto
+```protobuf
 message TracePacket {
   optional uint64 timestamp = 8;
 
diff --git a/docs/embedder-guide.md b/docs/embedder-guide.md
index fbe3be3..fa47cea 100644
--- a/docs/embedder-guide.md
+++ b/docs/embedder-guide.md
@@ -15,11 +15,11 @@
 - Point out the relevant GN targets:
   `//src/tracing`, `//src/tracing:ipc`, `//src/ipc`.
 - Explain the API surface:
-  - [producer.h](/include/perfetto/tracing/core/producer.h)
-  - [consumer.h](/include/perfetto/tracing/core/consumer.h)
-  - [service.h](/include/perfetto/tracing/core/tracing_service.h)
+  - [producer.h](/include/perfetto/ext/tracing/core/producer.h)
+  - [consumer.h](/include/perfetto/ext/tracing/core/consumer.h)
+  - [service.h](/include/perfetto/ext/tracing/core/tracing_service.h)
 - Explain the ABI surface:
-  - [shared_memory_abi.h](/include/perfetto/tracing/core/shared_memory_abi.h)
+  - [shared_memory_abi.h](/include/perfetto/ext/tracing/core/shared_memory_abi.h)
   - IPC's [wire protocol](/protos/perfetto/ipc/wire_protocol.proto) (if used)
   - The input [config protos](/protos/perfetto/config)
   - The output [trace protos](/protos/perfetto/trace)
diff --git a/docs/heapprofd-design.md b/docs/heapprofd-design.md
new file mode 100644
index 0000000..f5f74cb
--- /dev/null
+++ b/docs/heapprofd-design.md
@@ -0,0 +1,304 @@
+# heapprofd: Android Heap Profiler
+
+_**Status:** COMPLETED **·** fmayer, primiano **·** 2018-06-15_  
+_**Updated:** 2020-04-20_
+
+## Objective
+Provide a low-overhead native heap profiling mechanism, with C++ and Java callstack attribution, usable by all processes on an Android system. This includes Java and native services. The mechanism is capable of exporting heap dumps into traces in order to be able to correlate heap information with other activity on the system. This feature was added in the Android 10 release.
+
+## Overview
+![](images/heapprofd-design/Architecture.png)
+
+Implement an out-of-process heap profiler. Do the minimal amount of processing in-line of malloc, and then delegate to a central component for further processing. This introduces a new daemon _heapprofd_.
+
+When tracing is enabled, either via a system property or a signal delivered to an existing process, a given percentage of malloc calls copies the current call stack into a shared memory buffer that is received by heapprofd. heapprofd uses libunwindstack asynchronously for stack unwinding and symbolization. This information is used to build bookkeeping tables to track live allocations and is ultimately dumped into the Perfetto trace.
+
+All data referenced in this design document was collected on a Pixel 2 on Android P.
+
+
+### Requirements
+These are the properties that must be fulfilled by a heap profiler for Android:
+
+**No setup:** A heap profile can be taken with a single command.
+
+**Profile running apps:** The system can be used to enable profiling of already running apps to get information of memory usage _since the profiling was enabled_ without requiring a restart. This can be useful to track down memory leaks.
+
+**Attribute to Java methods:** The system can be used to track the native memory usage of Java applications. Allocations on the Java heap are outside of the scope of this work.
+
+**Zero overhead when disabled:** the system must not incur a performance overhead when it is not enabled.
+
+**Profile whole system:** the system must be capable of handling the load of profiling all processes running. The sample rate is adjusted to limit the amount of data.
+
+**Negligible in-process memory overhead:** the system must not hold bookkeeping data in the process in order not to inflate higher-level metrics like PSS.
+
+**Bounded performance impact:** the device must still be useable for all use-cases.
+
+
+## Detailed Design
+
+### Enabling profiling
+
+#### Use case 1: profiling future allocations from a running process
+One of the real-time signals ([`BIONIC_SIGNAL_PROFILER`](https://cs.android.com/android/platform/superproject/+/master:bionic/libc/platform/bionic/reserved_signals.h?q=symbol:BIONIC_SIGNAL_PROFILER)) is reserved in libc as a triggering mechanism. In this scenario:
+
+*   heapprofd sends a RT signal to the target process
+*   Upon receival of the signal, bionic reacts by installing a temporary malloc hook, which in turn spawns a thread to dynamically load libheapprofd.so in the process context. This means heapprofd will not work for statically linked binaries, as they lack the abilitity to `dlopen`. We can not spawn the thread directly from the signal handler, as `pthread_create` is not async-safe.
+*   The initializer in libheapprofd.so is called to take care of the rest (see [client operation](#client-operation-and-in-process-hooks) below)
+
+
+#### Use case 2: profiling a single process from startup
+*   heapprofd sets a property of the form libc.debug.heapprofd.argv0 (argv0 being the first argument in `/proc/self/cmdline`, up to the first ":")
+*   Native processes: when bionic is initialized checks for the presence of the property and, if found and matches the process name, loads the libheapprofd.so.
+*   Managed java processes: zygote calls `mallopt(M_SET_ZYGOTE_CHILD, ...)` in `PreApplicationInit`. In this, Bionic checks for the presence of the property and, if found and matches the process name, loads the libheapprofd.so and continues as above.
+
+#### Use case 3: profiling the whole system
+A system property `libc.heapprofd.enable` can be set to enable heap profiling on startup. When this property is set every process on startup will load the libheapprofd.so library. The rest is identical to the case above.
+
+
+### Disabling profiling
+Disabling profiling happens simply by virtue of shutting down the sockets from the heapprofd end. Upon send() failure the client will uninstall the hooks (see appendix: thread-safe hooks setup / teardown)
+
+
+### Client operation and in process hooks
+Upon initialization of libheapprofd.so:
+
+*   The client establishes a connection to the heapprofd daemon through a connected UNIX socket.
+*   Upon connection, the daemon will send a packet to the client specifying the profiling configuration (sampling rate; sample all threads / only specific threads; tuning of sampling heuristics). It will also send an FD for the SharedMemoryBuffer used to send samples  (see [wire protocol](heapprofd-wire-protocol.md)).
+*   The malloc hooks are installed.
+
+Upon each `*alloc()/posix_memalign()` call, the client library will perform some minimal bookkeeping. If the sampling	 rate is hit, it will copy the raw stack, together with a header specifying register state, tid, a global sequence number of the operation, and size of the allocation to the shared memory buffer. It will then send on a control socket to wake up the service.
+
+Upon each `free()` call, the client will append the freed address into a global (process-wide) append-only buffer (the buffer is to avoid the overhead of a send() for each free). This buffer of free()s is sent to the heapprofd daemon when the fixed-size buffer is full or after a preset number of operations. This also includes a global sequence number for the operation.
+
+If the send() fails because the heapprofd has shut down the socket, voluntarily (graceful disabling) or involuntarily (has crashed) the client will teardown the hooks and disabling any profiling operation.
+
+
+### Service operation
+![](images/heapprofd-design/shmem-detail.png)
+
+The unwinder thread read the client's shared memory buffers and handle the samples received. The result of the unwinding is then enqueued using a PostTask for the main thread to do the accounting. A queue-based model between the threads is chosen because it makes synchronization easier. No synchronization is needed at all in the main thread, as the bookkeeping data will only be accessed by it.
+
+If the sample is a malloc, the stack is unwound and the resulting data is handled in the main thread. The main thread ignores mallocs with sequence numbers lower than the one already processed for this address. If the sample is a free, it is added to a buffer. As soon as all mallocs with a sequence number lower than the free have been handled, it is processed.
+
+
+#### Unwinding
+libunwindstack is used for unwinding. A new Memory class is implemented that overlays the copied stack over the process memory (which is accessed using FDMemory). FDMemory uses read on `/proc/self/mem` file descriptors sent by the target application.
+
+```
+class StackMemory : public unwindstack::MemoryRemote {
+ public:
+  ...
+  size_t Read(uint64_t addr, void* dst, size_t size) override {
+    if (addr >= sp_ && addr + size <= stack_end_ && addr + size > sp_) {
+      size_t offset = static_cast<size_t>(addr - sp_);
+      memcpy(dst, stack_ + offset, size);
+      return size;
+    }
+
+    return mem_->Read(addr, dst, size);
+  }
+
+ private:
+  uint64_t sp_;
+  uint8_t* stack_;
+  size_t size_;
+};
+```
+
+This allows unwinding to work both for native code and all three execution modes of ART. Native libraries are mapped into the process memory, and ephemeral debug information written by ART is also accessible through the process memory. There is a chance that ART will garbage collect the information before the unwinding is done, in which case we will miss stack frames. As this is a sampling approach anyway, that loss of accuracy is acceptable.
+
+Remote unwinding also enables us to use _global caching_ (`Elf::SetCachingEnabled(true)`) in libunwindstack. This prevents debug information being used by different processes to be loaded and decompressed multiple times.
+
+We add an `FDMaps` objects to parse maps from `/proc/self/maps` sent by the target process. We keep `FDMaps` object cached per process that is being profiled. This both saves the overhead of text-parsing `/proc/[pid]/maps` as well as keeps various objects needed for unwinding (e.g. decompressed minidebuginfo). In case an unwind fails with `ERROR_INVALID_MAP` we reparse the maps object. We will  do changes to libunwindstack to create a more general version of [`LocalUpdatableMaps`](https://cs.android.com/android/platform/superproject/+/master:system/core/libunwindstack/Maps.cpp?q=symbol:LocalUpdatableMaps) that is also applicable for remote processes.
+
+
+#### Advantages of remote unwinding
+
+**Crash-proofness:** Crashing bugs in the bookkeeping logic or libunwindstack do not result in user-visible crashes but only lack of profiling data. It will result in the connections to heapprofd being broken and profiling gracefully stopping on the client-side.
+
+**Performance:** copying the stack has much more consistent and higher performance than unwinding, which can take multiple milliseconds. See graph above.
+
+**Does not inflate higher-level metrics:** higher-level metrics such as PSS are not inflated by the book-keeping cost.
+
+**Compression:** bookkeeping of unwound frames can be more efficient if it is shared between multiple processes. E.g. common sequences of frames (in libc, ART, etc) can be deduped.
+
+
+#### Disadvantages of remote unwinding
+**Complexity:** the system has higher complexity than unwinding and symbolizing synchronously.
+
+#### Bookkeeping
+The data is stored as a tree where each element has a back-pointer to its parent. This deduplicates repeated stack frames.  String interning is applied for method names and library names.
+
+Details will change to adapt to data collected during the implementation.
+
+
+### Wire protocol
+In early versions of heapprofd, we used a `SOCK_STREAM` socket to send callstacks to the service. We now use a shared memory based [wire protocol](heapprofd-wire-protocol.md) described in detail separately.
+
+### Failure modes
+**heapprofd unwinding cannot keep up:** The shared memory buffer will reject new samples. If `block_client` is set, the client will retry until there is space in the shared memory buffer.
+
+**heapprofd crashes:** Writing on the control socket will fail, and the client will be torn down.
+
+**Writing in client fails:** If the write fails with any error code except `EINTR`, the connection is closed, and profiling is torn down.
+
+
+### Fork handling
+After a process forks, we need to clean up the state that was initialized by the parent process and uninstall the malloc hooks. We do not intend to currently support following forks, see [Alternatives Considered](#alternatives-considered) for possible implementation thereof.
+
+## Performance considerations
+
+### Remote unwinding
+_**Note:** This data was collected when heapprofd used a socket to communicate from client to service. We now use a shared-memory buffer, so we should be even lower overhead._
+
+Remote unwinding is used to reduce the performance impact on the applications that are being profiled. After the stack has been sent, the application can resume its operation while the remote daemon unwinds the stack and does unwinding. As sending the stack is, on average, a faster operation than unwinding the stack, this results in a performance gain.
+
+
+<table>
+  <tr>
+   <td>
+
+![](images/heapprofd-design/Android-Heap0.png)
+
+   </td>
+   <td>
+
+![](images/heapprofd-design/Android-Heap1.png)
+
+   </td>
+  </tr>
+</table>
+
+**Mean unwind:** 413us  
+**Mean send:** 50us  
+**Median unwind:** 193us  
+**Median send:** 14us  
+**90 percentile unwind:** 715us  
+**90 percentile send:** 40us
+
+
+### Sampling
+Unwinding the stack on every `malloc` call has a high cost that is not always worth paying. Thus malloc calls are sampled client-side using Poisson sampling with a probability proportional to their allocation size (i.e. larger allocations are more likely to be considered than small ones). All memory allocated since the last malloc considered is attributed to this allocation.
+
+The sampling rate is configurable as part of the initial handshake. A sampling rate == 1 will degenerate into the fully-accurate high-overhead mode.
+
+Prior art: [crbug.com/812262](http://crbug.com/812262), [crbug.com/803276](http://crbug.com/803276).
+
+## Implementation Plan
+### Implement prototype [done]
+Implement a prototype of the system described above that works with SELinux `setenforce 0` and running as root on walleye.
+
+### Implement Benchmark [done]
+Implement a program that executes malloc / free calls from ground truth data. Profile this program using heapprofd, then compare results to ground truth data. Use this to iterate on sampling heuristics.
+
+### Productionize [done]
+Do security changes required to run heapprofd with `setenforce 1` and as non-root.
+
+
+## Testing Plan
+
+* Employ fuzzing on the shared memory buffer. [done]
+* Unit-tests for components. [done]
+* CTS. [done]
+
+
+## Background
+
+### ART modes of execution
+ART (Android RunTime, the Android Java Runtime) has three different modes of execution.
+
+**Interpreted:** Java byte-code is interpreted during execution. Instrumentation in ART allows to get dexpc (~offset from dex file) for the code being executed.
+
+**JIT-compiled:** Java byte-code is compiled to native code during run-time. Both the code and the ELF information only live in process memory. The debug information is stored in a global variable, currently only if the app is debuggable or a global system property (`dalvik.vm.minidebuginfo`) is set. This is because the current implementation incurs a memory overhead that is too high to default enable.
+
+**AOT (ahead of time) compiled:** Java code is compiled into native code before run-time. This produces an .oat file, which is essentially an .so. Both code and ELF information is stored on disk. During execution, like shared native libraries, it is memory mapped into process memory.
+
+### Stack Unwinding
+Stack unwinding is the process of determining the chain of return addresses from the raw bytes of the stack. These are the addresses we want to attribute the allocated memory to.
+
+The most efficient way of stack unwinding is using frame pointers. This is unreliable on Android as we do not control build parameters for vendor libraries or OEM builds and due to issues on ARM32. Thus, our stack unwinding relies on libunwindstack which uses DWARF information from the library ELF files to determine return addresses. This can significantly slower, with unwinding of a stack taking between 100μs and ~100 ms ([data from simpleperf](https://gist.github.com/segfaulthunter/a3a5a352196f9037f34241f8fb09004d)).
+
+[libunwindstack](https://cs.android.com/android/platform/superproject/+/master:system/core/libunwindstack/) is Android's replacement for [libunwind](https://www.nongnu.org/libunwind/). It has a modern C++ object-oriented API surface and support for Android specific features allowing it to unwind mixed native and Java applications using information emitted by ART depending on execution mode. It also supports symbolization for native code and all three execution modes or ART.
+
+### Symbolization
+Symbolization is the process of determining function name and line number from a code address. For builds by Google, we can get symbolized binaries (i.e. binaries with an ELF section that can be used for symbolization) from go/ab or https://ci.android.com (e.g. https://ci.android.com/builds/submitted/6410994/aosp_cf_x86_phone-userdebug/latest/aosp_cf_x86_phone-symbols-6410994.zip).
+
+For other builds, symbolization requires debug info contained within the binary. This information is often compressed. Symbolization of JIT-ed code requires information contained in process memory.
+
+### Perfetto
+[Perfetto](https://perfetto.dev) is an open-source, highly efficient and expandable platform-wide tracing system that allows collection of performance data from kernel, apps and services. It aims to become the next-gen performance tracing mechanism for both Android and Chrome.
+
+
+## Related Work
+
+### simpleperf
+Even though [simpleperf](https://cs.android.com/android/platform/superproject/+/master:system/extras/simpleperf/doc/README.md) is a CPU rather than memory profiler, it is similar in nature to the work proposed here in that it supports offline unwinding. The kernel is asked to provide copies of stack traces at regular intervals, which are dumped onto disk. The dumped information is then used to unwind the stacks after the profiling is complete.
+
+
+### malloc-debug
+[malloc-debug](https://cs.android.com/android/platform/superproject/+/master:bionic/libc/malloc_debug/) instruments bionic's allocation functions to detect common memory problems like buffer overflows, double frees, etc. This is similar to the project described in this document as it uses the same mechanism to instrument the libc allocation functions. Unlike heapprofd, it does not provide the user with heap dumps.
+
+
+### Feature Matrix
+|              | use after free detection | Java object graph attribution | native memory attribution | Android | out-of-process |
+|--------------|--------------------------|-------------------------------|---------------------------|---------|----------------|
+| heapprofd    | no                       | no                            | yes                       | yes     | yes            |
+| malloc-debug | yes                      | no                            | yes                       | yes     | no             |
+
+## Alternatives Considered
+
+### Copy-on-write stack
+The lower frames of the stack are unlikely to change between the client sending and the server unwinding the stack information. We wanted to exploit this fact by marking the stack pages as copy-on-write by [`vmsplice(2)`](http://man7.org/linux/man-pages/man2/vmsplice.2.html)-ing them into a pipe. Unfortunately, the vmsplice system call does not mark pages as copy-on-write, but is conceptually a mmap into the pipe buffer, which causes the daemon to see changes to the stack that happen after the vmsplice and hence corrupt the unwinder.
+
+
+### Profiling across fork(2)
+If we want to enable profiling for the newly forked process, we need to establish new connections to heapprofd and create a new connection pool. This is to prevent messages from the parent and child process from being interleaved.
+
+For non-zygote processes, we could use [`pthread_atfork(3)`](http://man7.org/linux/man-pages/man3/pthread_atfork.3.html) to establish new connections.
+
+For zygote processes, [`FileDescriptorInfo::ReopenOrDetach`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/jni/fd_utils.cpp?q=%22void%20FileDescriptorInfo::ReopenOrDetach%22), which is called after `fork(2)`– and thus after the `pthread_atfork` handlers – detaches all sockets, i.e. replaces them with file descriptors to `/dev/null`. If the socket is not contained within [`kPathWhiteList`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/jni/fd_utils.cpp?q=symbol:kPathWhitelist), zygote crashes instead. Thus using only a `pthread_atfork` handler is not feasible, as the connections established within will immediately get disconnected in zygote children.
+
+After forking, zygote calls [`PreApplicationInit`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/jni/com_android_internal_os_Zygote.cpp?q=symbol:PreApplicationInit), which is currently used by malloc\_debug to detect whether it is in the root zygote or in a child process by setting `gMallocLeakZygoteChild`. It also calls [Java callbacks](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/jni/com_android_internal_os_Zygote.cpp?q=CallStaticVoidMethod.*gCallPostForkChildHooks), but there does not seem to currently exist a way to dynamically register native callbacks.
+
+Naive lazy initialization (i.e. closing the socket in the atfork handler, and then reconnecting on the first call to malloc) is problematic, as the code in zygote between fork and `ReopenOrDetach` might call `malloc`, thus leading to the connection to be established, which then gets closed by `ReopenOrDetach` again.
+
+To solve this, we can take an approach similar to `gMallocLeakZygoteChild`. Before forking, zygote will be modified to set `gheapprofdInZygoteFork` to true, and after the fork handling is finished it will be set to false. This way we can make sure we delay the lazy initialization until the fork is fully complete. `pthread_atfork` is used to close the file descriptors after fork in the child.
+
+
+### Profiling app from startup by externally detecting startup
+This option relies on the ability of the tracing system to detect app startup (which we'll need regardless for perf profiling).
+
+**Advantages**
+*   one case fewer to handle from the libc viewpoint
+
+**Disadvantages**
+*   less accurate, will miss the first X ms of startup
+*   a mechanism that watches ftrace events to detect startup is non-trivial.
+
+### Delayed Unwinding
+Anticipating that many allocations are short-lived, we can delay the unwinding of stacks by a fixed time. This is a memory vs CPU usage trade-off, as these stacks have to be held in memory until they are either unwound or freed.
+
+This graph shows that 20 % of allocations are freed within 900 sampled allocations (at 1 %, so 500000 total) from the same process.
+
+
+<table>
+  <tr>
+   <td>
+
+![](images/heapprofd-design/Android-Heap2.png)
+
+<p>
+<strong>Mean:</strong> 7000 allocations
+   </td>
+   <td>
+
+![](images/heapprofd-design/Android-Heap3.png)
+
+<p>
+<strong>Mean:</strong> 8950 bytes
+   </td>
+  </tr>
+</table>
+
+
+So, at 1 % sampling rate, for at a cost of ~8 megabytes (900 \* 8950) per process, we can reduce the number of unwinds by around 20 %. This will not allow us to get an accurate number of "allocated space", so this idea was rejected.
diff --git a/docs/heapprofd-wire-protocol.md b/docs/heapprofd-wire-protocol.md
new file mode 100644
index 0000000..65ec187
--- /dev/null
+++ b/docs/heapprofd-wire-protocol.md
@@ -0,0 +1,92 @@
+# heapprofd: Shared Memory
+
+_**Status**: Implemented_  
+_**Authors**: fmayer_  
+_**Reviewers**: rsavitski, primiano_  
+_**Last Updated**: 2019-02-11_
+
+## Objective
+Restructure heapprofd to make use of the <code>[SharedRingBuffer](https://cs.android.com/android/platform/superproject/+/master:external/perfetto/src/profiling/memory/shared_ring_buffer.cc)</code>.
+
+
+## Overview
+Instead of using a socket pool for sending callstacks and frees to heapprofd, we use a single shared memory buffer and signaling socket. The client writes the record describing mallocs or frees into the shared memory buffer, and then sends a single byte on the signalling socket to wake up the service.
+
+![](images/heapprofd-design/shmem-overview.png)
+
+## High-level design
+Using a shared memory buffer between the client and heapprofd removes the need to drain the socket as fast as possible in the service, which we needed previously to make sure we do not block the client's malloc calls. This allows us to simplify the threading design of heapprofd.
+
+The _Main Thread_ has the Perfetto producer connection to traced, and handles incoming client connections for `/dev/socket/heapprofd`. It executes the logic for finding processes matching an incoming TraceConfig, matching processes to client configurations, and does the handshake with the clients. During this handshake the shared memory buffer is created by the service. After the handshake is done, the socket for a client is handed off to a particular _Unwinder Thread_.
+
+After the handshake is completed, the sockets are handled by the assigned _Unwinder Thread's_ eventloop. The unwinder thread owns the metadata required for unwinding (the `/proc/pid/{mem,maps}` FDs, derived libunwindstack objects and the shared memory buffer). When data is received on the signaling socket, the _Unwinding Thread_ unwinds the callstack provided by the client and posts a task to the _Main Thread_ to apply to bookkeeping. This is repeated until no more records are pending in the buffer.
+
+To shut down a tracing session, the _Main Thread_ posts a task on the corresponding _Unwinding Thread_ to shut down the connection. When the client has disconnected, the _Unwinding Thread_ posts a task on the _Main Thread_ to inform it about the disconnect. The same happens for unexpected disconnects.
+
+![](images/heapprofd-design/shmem-detail.png)
+
+### Ownership
+At every point in time, every object is owned by exactly one thread. No references or pointers are shared between different threads.
+
+**_Main Thread:_**
+
+*   Signalling sockets before handshake was completed.
+*   Bookkeeping data.
+*   Set of connected processes and TraceConfigs (in `ProcessMatcher` class).
+
+**_Unwinder Thread, for each process:_**
+
+*   Signalling sockets after handshake was completed.
+*   libunwindstack objects for `/proc/pid/{mem,maps}`.
+*   Shared memory buffer.
+
+
+## Detailed design
+Refer to the following phases in the sequence diagram below:
+
+### 1. Handshake
+The _Main Thread_ receives a `TracingConfig` from traced containing a `HeapprofdConfig`. It adds the processes expected to connect, and their `ClientConfiguration` to the `ProcessMatcher`. It then finds matching processes (by PID or cmdline) and sends the heapprofd RT signal to trigger initialization.
+
+The processes receiving this configuration connect to `/dev/socket/heapprofd` and sends `/proc/self/{map,mem}` fds. The _Main Thread_ finds the matching configuration in the `ProcessMatcher`, creates a new shared memory buffer, and sends the two over the signalling socket. The client uses those to finish initializing its internal state. The _Main Thread_ hands off (`RemoveFiledescriptorWatch` + `AddFiledescriptorWatch`) the signalling socket to an _Unwinding Thread_. It also hands off the `ScopedFile`s for the `/proc` fds. These are used to create `UnwindingMetadata`.
+
+
+### 2. Sampling
+Now that the handshake is done, all communication is between the _Client_ and its corresponding _Unwinder Thread_.
+
+For every malloc, the client decides whether to sample the allocation, and if it should, write the `AllocMetadata` + raw stack onto the shared memory buffer, and then sends a byte over the signalling socket to wake up the _Unwinder Thread_. The _Unwinder Thread_ uses `DoUnwind` to get an `AllocRecord` (metadata like size, address, etc + a vector of frames).  It then posts a task to the _Main Thread_ to apply this to the bookkeeping.
+
+
+### 3. Dump / concurrent sampling
+A dump request can be triggered by two cases:
+
+*   a continuous dump
+*   a flush request from traced
+
+Both of these cases are handled the same way. The _Main Thread_ dumps the relevant processes' bookkeeping and flushes the buffers to traced.
+
+In general, _Unwinder Threads_ will receive concurrent records from clients. They will continue unwinding and posting tasks for bookkeeping to be applied. The bookkeeping will be applied after the dump is done, as the bookkeeping data cannot be concurrently modified.
+
+
+### 4. Disconnect
+traced sends a `StopDataSource` IPC. The _Main Thread_ posts a task to the _Unwinder Thread_ to ask it to disconnect from the client. It unmaps the shared memory, closes the memfd, and then closes the signalling socket.
+
+The client receives an `EPIPE` on the next attempt to send data over that socket, and then tears down the client.
+
+![shared memory sequence diagram](images/heapprofd-design/Shared-Memory0.png "shared memory sequence diagram")
+
+
+## Changes to client
+The client will no longer need a socket pool, as all operations are done on the same shared memory buffer and the single signalling socket. Instead, the data is written to the shared memory buffer, and then a single byte is sent on the signalling socket in nonblocking mode.
+
+We need to be careful about which operation we use to copy the callstack to the shared memory buffer, as `memcpy(3)` can crash on the stack frame guards due to source hardening.
+
+
+## Advantages over current design
+*   Clear ownership semantics between threads.
+*   No references or pointers are passed between threads.
+*   More efficient client using fewer locks.
+
+
+## Disadvantages over current design
+*   Inflates target process PSS / RSS with the shared memory buffer.
+*   TaskRunners are unbounded queues. This has the potential of enqueueing a lot of bookkeeping work for a pathologically behaving process. As applying the bookkeeping information is a relatively cheap operation, we accept that risk.
diff --git a/docs/heapprofd.md b/docs/heapprofd.md
index 5abb440..b10fec9 100644
--- a/docs/heapprofd.md
+++ b/docs/heapprofd.md
@@ -33,6 +33,12 @@
 
 This will create a pprof-compatible heap dump when Ctrl+C is pressed.
 
+If you are having problems, run the following command and try again:
+
+```
+$ adb shell setprop persist.traced.enable 1
+```
+
 You can also use the [Perfetto UI](https://ui.perfetto.dev/#!/record?p=memory)
 to record heapprofd profiles. Tick "Heap profiling" in the trace configuration,
 enter the processes you want to target, click "Add Device" to pair your phone,
@@ -166,12 +172,28 @@
 enumerated in the output directory.
 
 ## Symbolization
-If the profiled binary or libraries do not have debug symbols, you can
-symbolize profiles offline. All tools (traceconv, trace_processor_shell,
-the heap_profile script) support specifying `PERFETTO_BINARY_PATH` as an
-environment variable.
+**Symbolization is currently only available on Linux.**
 
-For instance, run
+### Set up llvm-symbolizer
+You only need to do this once.
+
+To use symbolization, your system must have llvm-symbolizer installed and
+accessible from `$PATH` as `llvm-symbolizer`. On Debian, you can install it
+using `sudo apt install llvm-9`.
+This will create `/usr/bin/llvm-symbolizer-9`. Symlink that to somewhere in
+your `$PATH` as `llvm-symbolizer`. For instance.
+
+For instance, `ln -s /usr/bin/llvm-symbolizer-9 ~/bin/llvm-symbolizer`, and
+add `~/bin` to your path (or run the commands below with `PATH=~/bin:$PATH`
+prefixed).
+
+### Symbolize your profile
+
+If the profiled binary or libraries do not have symbol names, you can
+symbolize profiles offline. Even if they do, you might want to symbolize in
+order to get inlined function and line number information. All tools
+traceconv, trace_processor_shell, the heap_profile script) support specifying
+the `PERFETTO_BINARY_PATH` as an environment variable.
 
 ```
 PERFETTO_BINARY_PATH=somedir tools/heap_profile --name ${NAME}
@@ -206,6 +228,25 @@
 If you see a callstack that seems to impossible from looking at the code, make
 sure no [DEDUPED frames](#deduped-frames) are involved.
 
+
+### Symbolization: Could not find library
+
+When symbolizing a profile, you might come accross messages like this:
+
+```
+Could not find /data/app/invalid.app-wFgo3GRaod02wSvPZQ==/lib/arm64/somelib.so
+(Build ID: 44b7138abd5957b8d0a56ce86216d478).
+```
+
+Check whether your library (in this example somelib.so) exists in
+`PERFETTO_BINARY_PATH`. Then compare the Build ID to the one in your
+symbol file, which you can get by running
+`readelf -n /path/in/binary/path/somelib.so`. If it does not match, the
+symbolized file has a different version than the one on device, and cannot
+be used for symbolization.
+If it does, try moving somelib.so to the root of `PERFETTO_BINARY_PATH` and
+try again.
+
 ## Known Issues
 
 ### Android 10
@@ -275,7 +316,7 @@
 
 echo '
 buffers {
-  size_kb: 100024
+  size_kb: 102400
 }
 
 data_sources {
diff --git a/docs/images/annotation-counter.png b/docs/images/annotation-counter.png
new file mode 100644
index 0000000..b555537
--- /dev/null
+++ b/docs/images/annotation-counter.png
Binary files differ
diff --git a/docs/images/annotation-slice.png b/docs/images/annotation-slice.png
new file mode 100644
index 0000000..fb950f1
--- /dev/null
+++ b/docs/images/annotation-slice.png
Binary files differ
diff --git a/docs/images/description.png b/docs/images/description.png
new file mode 100644
index 0000000..80c63fc
--- /dev/null
+++ b/docs/images/description.png
Binary files differ
diff --git a/docs/images/heapprofd-design/Android-Heap0.png b/docs/images/heapprofd-design/Android-Heap0.png
new file mode 100644
index 0000000..b56685e
--- /dev/null
+++ b/docs/images/heapprofd-design/Android-Heap0.png
Binary files differ
diff --git a/docs/images/heapprofd-design/Android-Heap1.png b/docs/images/heapprofd-design/Android-Heap1.png
new file mode 100644
index 0000000..386a8af
--- /dev/null
+++ b/docs/images/heapprofd-design/Android-Heap1.png
Binary files differ
diff --git a/docs/images/heapprofd-design/Android-Heap2.png b/docs/images/heapprofd-design/Android-Heap2.png
new file mode 100644
index 0000000..999e7cb
--- /dev/null
+++ b/docs/images/heapprofd-design/Android-Heap2.png
Binary files differ
diff --git a/docs/images/heapprofd-design/Android-Heap3.png b/docs/images/heapprofd-design/Android-Heap3.png
new file mode 100644
index 0000000..7850b35
--- /dev/null
+++ b/docs/images/heapprofd-design/Android-Heap3.png
Binary files differ
diff --git a/docs/images/heapprofd-design/Architecture.png b/docs/images/heapprofd-design/Architecture.png
new file mode 100644
index 0000000..71d688c
--- /dev/null
+++ b/docs/images/heapprofd-design/Architecture.png
Binary files differ
diff --git a/docs/images/heapprofd-design/Service.png b/docs/images/heapprofd-design/Service.png
new file mode 100644
index 0000000..7f76a2e
--- /dev/null
+++ b/docs/images/heapprofd-design/Service.png
Binary files differ
diff --git a/docs/images/heapprofd-design/Shared-Memory0.png b/docs/images/heapprofd-design/Shared-Memory0.png
new file mode 100644
index 0000000..870192e
--- /dev/null
+++ b/docs/images/heapprofd-design/Shared-Memory0.png
Binary files differ
diff --git a/docs/images/heapprofd-design/shmem-detail.png b/docs/images/heapprofd-design/shmem-detail.png
new file mode 100644
index 0000000..d40b585
--- /dev/null
+++ b/docs/images/heapprofd-design/shmem-detail.png
Binary files differ
diff --git a/docs/images/heapprofd-design/shmem-overview.png b/docs/images/heapprofd-design/shmem-overview.png
new file mode 100644
index 0000000..6138089
--- /dev/null
+++ b/docs/images/heapprofd-design/shmem-overview.png
Binary files differ
diff --git a/docs/java-hprof.md b/docs/java-hprof.md
new file mode 100644
index 0000000..b7eef31
--- /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: 102400
+  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/life-of-a-tracing-session.md b/docs/life-of-a-tracing-session.md
index c645e41..be07e02 100644
--- a/docs/life-of-a-tracing-session.md
+++ b/docs/life-of-a-tracing-session.md
@@ -29,12 +29,12 @@
 10. The producer creates one or more data source instance, as instructed in
     the previous step.
 11. Each data source instance creates one or more
-    [`TraceWriter`](/include/perfetto/tracing/core/trace_writer.h) (typically
+    [`TraceWriter`](/include/perfetto/ext/tracing/core/trace_writer.h) (typically
     one for each thread).
 12. Each `TraceWriter` writes one or more
     [`TracePacket`](/protos/perfetto/trace/trace_packet.proto).
 13. While doing so, each `TraceWriter` takes ownership of shared memory buffer's
-    chunks, using the [`SharedMemoryArbiter`](/include/perfetto/tracing/core/shared_memory_arbiter.h).
+    chunks, using the [`SharedMemoryArbiter`](/include/perfetto/ext/tracing/core/shared_memory_arbiter.h).
 14. While writing a `TracePacket`, the `TraceWriter` will unavoidably cross the
     chunk boundary (typically 4KB, but can configured to be smaller).
 15. When this happens the `TraceWriter` will release the current chunk and
@@ -52,7 +52,7 @@
 18. The service will check if the given chunk, identified by the tuple
     `{ProducerID (unspoofable), WriterID, ChunkID}` is still present in the
     trace buffer and if so will proceed to patch it (% sanity checks).
-19. The consumer sends a [`FlushRequest`](/perfetto/ipc/consumer_port.proto#52)
+19. The consumer sends a [`FlushRequest`](/protos/perfetto/ipc/consumer_port.proto#52)
     to the service, asking it commit all data on flight in the trace buffers.
 20. The service, in turn, issues a
     [`Flush`](/protos/perfetto/ipc/producer_port.proto#132) request to all
diff --git a/docs/long-traces.md b/docs/long-traces.md
index 0907e24..cc43a8f 100644
--- a/docs/long-traces.md
+++ b/docs/long-traces.md
@@ -45,7 +45,7 @@
 $ cd perfetto
 
 # Prepare for the build (as per instructions linked above).
-$ tools/install-build-deps --no-android
+$ tools/install-build-deps
 $ tools/gn gen out/mac_release --args="is_debug=false"
 
 # Compiles the textual protobuf into binary format
@@ -53,7 +53,7 @@
 $ tools/ninja -C out/mac_release/ long_trace.cfg.protobuf
 
 # Alternatively, the more verbose variant:
-$ protoc=$(pwd)/out/mac_release/gcc_like_host/protoc
+$ alias protoc=$(pwd)/out/mac_release/protoc
 $ protoc --encode=perfetto.protos.TraceConfig \
         -I$(pwd) \
         $(pwd)/protos/perfetto/config/perfetto_config.proto \
diff --git a/docs/multi-layer-tracing.md b/docs/multi-layer-tracing.md
index 48a51af..b344f56 100644
--- a/docs/multi-layer-tracing.md
+++ b/docs/multi-layer-tracing.md
@@ -11,7 +11,7 @@
 
 The TL;DR of the trick is:
 - ABI stability of the
-  [shared_memory_abi.h](/include/perfetto/tracing/core/shared_memory_abi.h)
+  [shared_memory_abi.h](/include/perfetto/ext/tracing/core/shared_memory_abi.h)
 - ABI stability of the IPC surface.
 
 The tracing service in chromium should proxy Producer connections (adapting Mojo
diff --git a/docs/recording-traces.md b/docs/recording-traces.md
new file mode 100644
index 0000000..1a5f0f1
--- /dev/null
+++ b/docs/recording-traces.md
@@ -0,0 +1,103 @@
+# Recording traces
+
+Perfetto provides a few different ways for recording traces:
+
+1. With the [Perfetto UI](#tracing-with-perfetto-ui). This is the most
+   convenient way to get started.
+
+2. With the [`perfetto` command line tool](#tracing-on-the-command-line) on
+   Android. This is a good match for automated testing.
+
+3. With the [Perfetto Client API](#tracing-with-the-api). This provides the
+   most control when Perfetto is integrated in your app.
+
+## Tracing with the API
+
+> The [Perfetto SDK example](https://github.com/skyostil/perfetto-sdk-example)
+> demonstrates trace recording through the API.
+
+> Tracing is currently only supported with the in-process Perfetto service
+> (perfetto::kInProcessBackend).
+
+In order to record a trace, you should first initialize a
+[TraceConfig](../protos/perfetto/config/trace_config.proto) message which
+specifies what type of data to record. If your app includes track events
+(i.e, `TRACE_EVENT`), you typically want to choose the categories which are
+enabled for tracing. By default, all non-debug categories are enabled, but
+you can enable a specific one like this:
+
+```C++
+perfetto::protos::gen::TrackEventConfig track_event_cfg;
+track_event_cfg.add_disabled_categories("*");
+track_event_cfg.add_enabled_categories("rendering");
+```
+
+Next, build the main trace config together with the track event part:
+
+```C++
+perfetto::TraceConfig cfg;
+cfg.add_buffers()->set_size_kb(1024);  // Record up to 1 MiB.
+auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+ds_cfg->set_name("track_event");
+ds_cfg->set_track_event_config_raw(track_event_cfg.SerializeAsString());
+```
+
+If your app includes a custom data source, you can also enable it here:
+
+```C++
+ds_cfg = cfg.add_data_sources()->mutable_config();
+ds_cfg->set_name("my_data_source");
+```
+
+Read more about [advanced trace config features](trace-config.md). After
+building the trace config, you can begin tracing:
+
+```C++
+std::unique_ptr<perfetto::TracingSession> tracing_session(
+    perfetto::Tracing::NewTrace());
+tracing_session->Setup(cfg);
+tracing_session->StartBlocking();
+```
+
+> Tip: API methods with `Blocking` in their name will suspend the calling
+> thread until the respective operation is complete. There are typically also
+> asynchronous versions of each function to that don't have this limitation.
+
+Now that tracing is active, instruct your app to perform the operation you
+want to record. After that, we can stop tracing and collect the
+protobuf-formatted trace data:
+
+```C++
+tracing_session->StopBlocking();
+std::vector<char> trace_data(tracing_session->ReadTraceBlocking());
+
+// Write the trace into a file.
+std::ofstream output;
+output.open("example.pftrace", std::ios::out | std::ios::binary);
+output.write(&trace_data[0], trace_data.size());
+output.close();
+```
+
+To save memory with longer traces, you can also tell Perfetto to write
+directly into a file by passing a file descriptor into Setup(), remembering
+to close the file after tracing is done:
+
+```C++
+int fd = open("example.pftrace", O_RDWR | O_CREAT | O_TRUNC, 0600);
+tracing_session->Setup(cfg, fd);
+tracing_session->StartBlocking();
+// ...
+tracing_session->StopBlocking();
+close(fd);
+```
+
+The resulting trace file can be directly opened in the [Perfetto
+UI](https://ui.perfetto.dev) or the [Trace Processor](trace-processor.md).
+
+## Tracing with Perfetto UI
+
+TODO(skyostil).
+
+## Tracing on the command line
+
+TODO(skyostil).
diff --git a/docs/security-model.md b/docs/security-model.md
index 5f38531..af5a2a1 100644
--- a/docs/security-model.md
+++ b/docs/security-model.md
@@ -16,9 +16,9 @@
 **Producers**  
 Producers are never trusted. We assume they will try their best to DoS / crash /
 exploit the tracing service. We do so at the
-[core/service_impl.cc](/src/tracing/core/service_impl.cc) so that the same
-level of security and testing is applied regardless of the embedder and the IPC
-transport.
+[core/tracing_service_impl.cc](/src/tracing/core/tracing_service_impl.cc) so
+that the same level of security and testing is applied regardless of the
+embedder and the IPC transport.
 
 **Tracing service**  
 - The tracing service has to validate all inputs.
diff --git a/docs/toc.md b/docs/toc.md
index 3aac647..6e36fce 100644
--- a/docs/toc.md
+++ b/docs/toc.md
@@ -2,14 +2,19 @@
   * [Contributing](contributing.md)
   * [Build instructions](build-instructions.md)
   * [Running tests](testing.md)
+* Instrumenting and tracing
+  * [App instrumentation](app-instrumentation.md)
+  * [Recording traces](recording-traces.md)
 * On-device tracer
   * [Running Perfetto](running.md)
   * [Capturing long traces](long-traces.md)
   * [Advanced trace config](trace-config.md)
   * [Running in detached mode](detached-mode.md)
   * [Native Heap Profiling](heapprofd.md)
-* Trace analysis
+  * [Java Heap Profiling](java-hprof.md)
+* Offline trace processing
   * [Trace processor](trace-processor.md)
+  * [Trace analysis](analysis.md)
   * [Trace-based metrics](metrics.md)
   * [Trace conversion](traceconv.md)
   * [Clock synchronization](clock-sync.md)
@@ -24,3 +29,5 @@
   * [Embedding Perfetto](embedder-guide.md)
   * [ProtoZero internals](protozero.md)
   * [IPC internals](ipc.md)
+  * [heapprofd Design](heapprofd-design.md)
+  * [heapprofd Design: Wire Protocol](heapprofd-wire-protocol.md)
diff --git a/docs/track-events.png b/docs/track-events.png
new file mode 100644
index 0000000..fafc268
--- /dev/null
+++ b/docs/track-events.png
Binary files differ
diff --git a/docs/track-timeline.png b/docs/track-timeline.png
new file mode 100644
index 0000000..c7e1dc8
--- /dev/null
+++ b/docs/track-timeline.png
Binary files differ
diff --git a/gn/BUILD.gn b/gn/BUILD.gn
index 7412aa3..2041bbc 100644
--- a/gn/BUILD.gn
+++ b/gn/BUILD.gn
@@ -82,8 +82,6 @@
     "PERFETTO_TP_LINENOISE=$enable_perfetto_trace_processor_linenoise",
     "PERFETTO_TP_HTTPD=$perfetto_tp_httpd",
     "PERFETTO_TP_JSON=$enable_perfetto_trace_processor_json",
-    "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",
   ]
@@ -96,23 +94,23 @@
     "{{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:check_build_deps",
       "//gn/standalone/libc++:deps",
       "//gn/standalone/sanitizers:deps",
     ]
+    if (is_android) {
+      public_deps += [ "//gn/standalone:check_build_deps_android" ]
+    }
   }
 }
 
@@ -139,6 +137,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",
   ]
 }
 
@@ -178,33 +182,33 @@
   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)" ]
+}
+
+config("system_protoc") {
+  libs = [ "protoc" ]  # This will link against libprotoc.so
+}
+
+config("system_protobuf") {
+  libs = [ "protobuf" ]  # This will link against libprotobuf.so
 }
 
 # protoc compiler library, it's used for building protoc plugins and by
@@ -212,25 +216,66 @@
 group("protoc_lib") {
   visibility = whitelisted_protobuf_full_deps
   if (current_toolchain == host_toolchain) {
-    public_deps = [
-      "${perfetto_protobuf_target_prefix}:protoc_lib",
-    ]
+    if (perfetto_use_system_protobuf) {
+      public_configs = [
+        ":system_protobuf",
+        ":system_protoc",
+        ":protobuf_gen_config",
+      ]
+    } else {
+      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",
-    ]
+    if (perfetto_use_system_protobuf) {
+      public_configs = [ ":system_protobuf" ]
+    } else {
+      public_deps = [ "${perfetto_protobuf_target_prefix}:protobuf_full" ]
+    }
   }
 }
 
 group("protobuf_lite") {
-  public_deps = [
-    "${perfetto_protobuf_target_prefix}:protobuf_lite",
+  if (perfetto_use_system_protobuf) {
+    public_configs = [ ":system_protobuf" ]
+  } else {
+    public_deps = [ "${perfetto_protobuf_target_prefix}:protobuf_lite" ]
+  }
+}
+
+# This config is applied to the .pb.{cc,h} generated by proto_library(). This
+# config is propagated up to the source sets that depend on generated proto
+# headers and proto libraries. Therefore this should stay as lean and clean as
+# possible in terms of -W-no* suppressions. Thankfully the autogenerated .pb.h
+# headers violate less warnings than the libprotobuf_* library itself.
+# This config is defined here (as opposed to //buildtools/BUILD.gn) so that when
+# perfetto_use_system_protobuf=true, the right compiler flags are passed.
+config("protobuf_gen_config") {
+  visibility = [ "*" ]  # This is injected by standalone/proto_library.gni
+  defines = [
+    "GOOGLE_PROTOBUF_NO_RTTI",
+    "GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
   ]
+  cflags = [
+    "-Wno-unknown-warning-option",
+    "-Wno-deprecated",
+    "-Wno-undef",
+    "-Wno-zero-as-null-pointer-constant",
+  ]
+
+  if (!perfetto_use_system_protobuf) {
+    cflags += [
+      # Using -isystem instead of include_dirs (-I), so we don't need to
+      # suppress warnings coming from libprotobuf headers. Doing so would mask
+      # warnings in our own code.
+      "-isystem",
+      rebase_path("../buildtools/protobuf/src", root_build_dir),
+    ]
+  }
 }
 
 # The Google C++ Benchmark library.
@@ -238,9 +283,7 @@
 if (enable_perfetto_benchmarks) {
   group("benchmark") {
     testonly = true
-    public_deps = [
-      "//buildtools:benchmark",
-    ]
+    public_deps = [ "//buildtools:benchmark" ]
   }
 }
 
@@ -248,42 +291,40 @@
 # standalone debug builds.
 if (perfetto_build_standalone && (is_linux || is_android)) {
   group("libbacktrace") {
-    public_deps = [
-      "//buildtools:libbacktrace",
-    ]
+    public_deps = [ "//buildtools:libbacktrace" ]
   }
 }
 
 if (enable_perfetto_trace_processor_sqlite) {
   group("sqlite") {
     if (perfetto_root_path == "//") {
-      public_deps = [
-        "//buildtools:sqlite",
-      ]
+      public_deps = [ "//buildtools:sqlite" ]
     } else {
-      public_deps = [
-        "//third_party/sqlite:sqlite",
-      ]
+      if (build_with_chromium) {
+        public_deps = [ "//third_party/sqlite:sqlite_dev" ]
+      } else {
+        public_deps = [ "//third_party/sqlite:sqlite" ]
+      }
       public_configs = [ ":sqlite_third_party_include_path" ]
     }
   }
 }
 
 config("sqlite_third_party_include_path") {
-  include_dirs = [ "//third_party/sqlite" ]
+  if (build_with_chromium) {
+    include_dirs = [ "//third_party/sqlite/dev" ]
+  } else {
+    include_dirs = [ "//third_party/sqlite" ]
+  }
 }
 
 if (enable_perfetto_trace_processor_json) {
   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" ]
     }
   }
 }
@@ -292,19 +333,22 @@
   # 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" ]
   }
 }
 
@@ -313,14 +357,10 @@
   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" ]
     }
   }
 }
@@ -329,8 +369,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 828a493..42aa474 100644
--- a/gn/perfetto.gni
+++ b/gn/perfetto.gni
@@ -153,13 +153,19 @@
 
   # 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.
-  enable_perfetto_trace_processor =
-      perfetto_build_standalone || build_with_chromium ||
-      is_perfetto_build_generator
+  if (!defined(enable_perfetto_trace_processor)) {
+    enable_perfetto_trace_processor =
+        perfetto_build_standalone || build_with_chromium ||
+        is_perfetto_build_generator
+  }
 
   # Enables base::Watchdog. Is supported only on Linux-based platforms.
   # gn/BUILD.gn further restricts this to OS_LINUX || OS_ANDROID when generating
@@ -219,15 +225,10 @@
       (is_linux || is_android || is_mac)
 
   # Enables JSON support in the trace processor. Required for JSON trace import
-  # and export. Importer support can also be disabled using
-  # |enable_perfetto_trace_processor_json_import|.
+  # and export.
   enable_perfetto_trace_processor_json =
       enable_perfetto_trace_processor && !perfetto_build_with_android
 
-  # Enables Fuchsia trace format support in trace processor.
-  enable_perfetto_trace_processor_fuchsia =
-      enable_perfetto_trace_processor && !(build_with_chromium && is_android)
-
   # Enables httpd RPC support in the trace processor.
   # Further per-OS conditionals are applied in gn/BUILD.gn.
   enable_perfetto_trace_processor_httpd =
@@ -240,11 +241,6 @@
 }
 
 declare_args() {
-  # Enables importer support for JSON traces in the trace processor.
-  enable_perfetto_trace_processor_json_import =
-      enable_perfetto_trace_processor_json &&
-      !(build_with_chromium && is_android)
-
   # Enables the trace_to_text tool.
   enable_perfetto_tools_trace_to_text =
       enable_perfetto_tools && enable_perfetto_trace_processor_sqlite
@@ -252,6 +248,13 @@
   # Allows to build the UI (TypeScript/ HTML / WASM)
   enable_perfetto_ui =
       perfetto_build_standalone && enable_perfetto_trace_processor_sqlite
+
+  # Skip buildtools dependency checks (needed for ChromeOS).
+  skip_buildtools_check = false
+
+  # Used by CrOS system builds. Uses the system version of protobuf
+  # from /usr/include instead of the hermetic one.
+  perfetto_use_system_protobuf = false
 }
 
 # +---------------------------------------------------------------------------+
diff --git a/gn/perfetto_benchmarks.gni b/gn/perfetto_benchmarks.gni
index 0571557..b70999a 100644
--- a/gn/perfetto_benchmarks.gni
+++ b/gn/perfetto_benchmarks.gni
@@ -17,11 +17,12 @@
 perfetto_benchmarks_targets = [
   "gn:default_deps",
   "src/base:benchmarks",
-  "src/traced/probes/ftrace:benchmarks",
   "src/trace_processor/containers:benchmarks",
   "src/trace_processor/tables:benchmarks",
-  "src/tracing:benchmarks",
   "src/traced/probes/ftrace/kallsyms:benchmarks",
+  "src/traced/probes/ftrace:benchmarks",
+  "src/tracing/core:benchmarks",
+  "src/tracing:benchmarks",
   "test:benchmark_main",
   "test:end_to_end_benchmarks",
 ]
diff --git a/gn/perfetto_check_build_deps.gni b/gn/perfetto_check_build_deps.gni
new file mode 100644
index 0000000..a2e5a03
--- /dev/null
+++ b/gn/perfetto_check_build_deps.gni
@@ -0,0 +1,39 @@
+# 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("perfetto.gni")
+
+# This template, when instantiated, runs
+# tools/install-build-deps --check-only ${args}.
+# It's used to ensure that deps are current before building.
+template("perfetto_check_build_deps") {
+  if (perfetto_build_standalone && !skip_buildtools_check) {
+    action(target_name) {
+      out_file = "$target_gen_dir/$target_name.check"
+      out_file_path = rebase_path(out_file, root_build_dir)
+      script = "${perfetto_root_path}tools/install-build-deps"
+      args = [ "--check-only=${out_file_path}" ]
+      args += invoker.args
+      inputs = []
+      if (defined(invoker.inputs)) {
+        inputs += invoker.inputs
+      }
+      outputs = [ out_file ]
+    }
+  } else {
+    group(target_name) {
+      not_needed(invoker, "*")
+    }
+  }
+}
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..abcb761 100644
--- a/gn/perfetto_host_executable.gni
+++ b/gn/perfetto_host_executable.gni
@@ -23,7 +23,10 @@
 template("perfetto_host_executable") {
   if (current_toolchain == host_toolchain) {
     executable(target_name) {
-      forward_variables_from(invoker, "*")
+      if (defined(invoker.configs)) {
+        configs += invoker.configs
+      }
+      forward_variables_from(invoker, "*", [ "configs" ])
     }
   } else {
     not_needed(invoker, "*", [ "testonly" ])
@@ -36,27 +39,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 3574664..aae3085 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) {
@@ -50,6 +57,10 @@
   ]
 }
 
+if (enable_perfetto_heapprofd || enable_perfetto_traced_perf) {
+  perfetto_unittests_targets += [ "src/profiling/common:unittests" ]
+}
+
 if (enable_perfetto_heapprofd) {
   perfetto_unittests_targets += [
     "src/profiling/memory:unittests",
diff --git a/gn/proto_library.gni b/gn/proto_library.gni
index 85b0e57..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") {
@@ -236,6 +248,7 @@
         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") {
@@ -248,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 79cdadf..9928a72 100644
--- a/gn/standalone/BUILD.gn
+++ b/gn/standalone/BUILD.gn
@@ -12,10 +12,20 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//gn/perfetto_check_build_deps.gni")
 import("//gn/standalone/android.gni")
 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",
@@ -96,6 +106,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" ]
@@ -227,7 +243,7 @@
 
   # Android will refuse to run executables if they aren't position independent.
   # Instead on Linux there isn't any need and they break ASan (goo.gl/paFR6K).
-  if (is_android) {
+  if (is_android || is_linux) {
     asmflags = [ "-fPIE" ]
     cflags = [ "-fPIE" ]
     ldflags += [ "-pie" ]
@@ -268,8 +284,15 @@
   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" ]
 }
+
+# Checks that tools/install-build-deps has been run since it last changed.
+perfetto_check_build_deps("check_build_deps") {
+  args = []
+}
+
+perfetto_check_build_deps("check_build_deps_android") {
+  args = [ "--android" ]
+}
diff --git a/gn/standalone/check_buildtool_exists.py b/gn/standalone/check_buildtool_exists.py
deleted file mode 100755
index f9f57bf..0000000
--- a/gn/standalone/check_buildtool_exists.py
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/usr/bin/env python
-# 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.
-""" Script to check whether a given buildtool folder exists.
-
-Prints a user-friendly message if it doesn't.
-"""
-
-import os
-import sys
-import argparse
-
-
-def main():
-  parser = argparse.ArgumentParser(description='Test path for existence')
-  parser.add_argument('path', help='Path to test for existence')
-  parser.add_argument('--touch', help='Touch this path on success')
-  args = parser.parse_args()
-
-  if not os.path.exists(args.path):
-    err = '\x1b[31mCannot find %s/%s\nRun tools/install-build-deps --ui\x1b[0m'
-    print >> sys.stderr, err % (os.path.abspath('.'), sys.argv[1])
-    return 127
-
-  if args.touch:
-    with open(args.touch, 'a'):
-      os.utime(args.touch, None)
-
-  return 0
-
-
-if __name__ == '__main__':
-  sys.exit(main())
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 75c2294..95948a3 100644
--- a/gn/standalone/proto_library.gni
+++ b/gn/standalone/proto_library.gni
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("../perfetto.gni")
+
 template("proto_library") {
   assert(defined(invoker.sources))
   proto_sources = invoker.sources
@@ -126,14 +128,20 @@
     outputs = get_path_info(protogens, "abspath")
 
     protoc_script = "//gn/standalone/protoc.py"
-    protoc_label = "//gn:protoc($host_toolchain)"
-    protoc_path = get_label_info(protoc_label, "root_out_dir") + "/protoc"
+
+    if (perfetto_use_system_protobuf) {
+      protoc_rebased_path = "protoc"  # from PATH
+    } else {
+      protoc_label = "//gn:protoc($host_toolchain)"
+      protoc_path = get_label_info(protoc_label, "root_out_dir") + "/protoc"
+      protoc_rebased_path = "./" + rebase_path(protoc_path, root_build_dir)
+    }
     args = [
       "./" + rebase_path(protoc_script, root_build_dir),
 
       # Path should be rebased because |root_build_dir| for current toolchain
       # may be different from |root_out_dir| of protoc built on host toolchain.
-      "./" + rebase_path(protoc_path, root_build_dir),
+      protoc_rebased_path,
       "--proto_path",
       rebase_path(proto_in_dir, root_build_dir),
     ]
@@ -174,13 +182,13 @@
 
     args += rebase_path(proto_sources, root_build_dir)
 
-    inputs = [
-      protoc_path,
-    ]
-
-    deps = [
-      protoc_label,
-    ]
+    if (!perfetto_use_system_protobuf) {
+      inputs = [ protoc_path ]
+      deps = [ protoc_label ]
+    } else {
+      inputs = []
+      deps = []
+    }
 
     # TODO(hjd): Avoid adding to deps here this.
     # When we generate BUILD files we need find the transitive proto,
@@ -226,20 +234,28 @@
       }
 
       public_configs += [
-        "//buildtools:protobuf_gen_config",
+        "//gn:protobuf_gen_config",
         ":$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/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/heapprofd.rc b/heapprofd.rc
index 56ca875..dd96325 100644
--- a/heapprofd.rc
+++ b/heapprofd.rc
@@ -31,5 +31,8 @@
 on property:traced.lazy.heapprofd=1
     start heapprofd
 
-on property:persist.heapprofd.enable=0 && property:traced.lazy.heapprofd=0
+on property:persist.heapprofd.enable="" && property:traced.lazy.heapprofd=""
     stop heapprofd
+
+on property:persist.heapprofd.enable=0
+    setprop persist.heapprofd.enable ""
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 dbc68b7..d6b9b3b 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
@@ -36,8 +36,6 @@
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_LINENOISE() (0)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_HTTPD() (0)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_JSON() (0)
-#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)
 
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 ce91468..4bf677e 100644
--- a/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
+++ b/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
@@ -36,8 +36,6 @@
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_LINENOISE() (1)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_HTTPD() (PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_ANDROID() || PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() || PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MACOSX())
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_JSON() (1)
-#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)
 
diff --git a/include/perfetto/base/compiler.h b/include/perfetto/base/compiler.h
index d40fb5b..8b4ccda 100644
--- a/include/perfetto/base/compiler.h
+++ b/include/perfetto/base/compiler.h
@@ -51,7 +51,7 @@
 #define PERFETTO_PRINTF_FORMAT(x, y) \
   __attribute__((__format__(__printf__, x, y)))
 #else
-#defien PERFETTO_PRINTF_FORMAT(x, y)
+#define PERFETTO_PRINTF_FORMAT(x, y)
 #endif
 
 namespace perfetto {
diff --git a/include/perfetto/base/task_runner.h b/include/perfetto/base/task_runner.h
index cf60401..040aab2 100644
--- a/include/perfetto/base/task_runner.h
+++ b/include/perfetto/base/task_runner.h
@@ -17,6 +17,8 @@
 #ifndef INCLUDE_PERFETTO_BASE_TASK_RUNNER_H_
 #define INCLUDE_PERFETTO_BASE_TASK_RUNNER_H_
 
+#include <stdint.h>
+
 #include <functional>
 
 #include "perfetto/base/export.h"
diff --git a/include/perfetto/ext/base/BUILD.gn b/include/perfetto/ext/base/BUILD.gn
index fd369cf..e2d29b5 100644
--- a/include/perfetto/ext/base/BUILD.gn
+++ b/include/perfetto/ext/base/BUILD.gn
@@ -34,10 +34,12 @@
     "string_utils.h",
     "string_view.h",
     "string_writer.h",
+    "subprocess.h",
     "temp_file.h",
     "thread_annotations.h",
     "thread_checker.h",
     "thread_task_runner.h",
+    "thread_utils.h",
     "unix_task_runner.h",
     "utils.h",
     "uuid.h",
@@ -51,7 +53,5 @@
     sources += [ "unix_socket.h" ]
   }
   public_configs = [ "../../../../gn:asan_instrumentation" ]
-  public_deps = [
-    "../../base",
-  ]
+  public_deps = [ "../../base" ]
 }
diff --git a/include/perfetto/ext/base/circular_queue.h b/include/perfetto/ext/base/circular_queue.h
index 72f14d1..de7db0e 100644
--- a/include/perfetto/ext/base/circular_queue.h
+++ b/include/perfetto/ext/base/circular_queue.h
@@ -52,8 +52,8 @@
    public:
     using difference_type = ptrdiff_t;
     using value_type = T;
-    using pointer = const T*;
-    using reference = const T&;
+    using pointer = T*;
+    using reference = T&;
     using iterator_category = std::random_access_iterator_tag;
 
     Iterator(CircularQueue* queue, uint64_t pos, uint32_t generation)
@@ -74,10 +74,17 @@
       return queue_->Get(pos_);
     }
 
+    const T* operator->() const {
+      return const_cast<CircularQueue<T>::Iterator*>(this)->operator->();
+    }
+
     T& operator*() { return *(operator->()); }
+    const T& operator*() const { return *(operator->()); }
+
+    value_type& operator[](difference_type i) { return *(*this + i); }
 
     const value_type& operator[](difference_type i) const {
-      return *(*this + i);
+      return const_cast<CircularQueue<T>::Iterator&>(*this)[i];
     }
 
     Iterator& operator++() {
diff --git a/include/perfetto/ext/base/lookup_set.h b/include/perfetto/ext/base/lookup_set.h
index 11bff8e..2e31a52 100644
--- a/include/perfetto/ext/base/lookup_set.h
+++ b/include/perfetto/ext/base/lookup_set.h
@@ -47,6 +47,8 @@
 
   bool Remove(const T& child) { return set_.erase(child); }
 
+  void Clear() { set_.clear(); }
+
   static_assert(std::is_const<U>::value, "key must be const");
 
  private:
diff --git a/include/perfetto/ext/base/metatrace_events.h b/include/perfetto/ext/base/metatrace_events.h
index e3cfb77..e846ed6 100644
--- a/include/perfetto/ext/base/metatrace_events.h
+++ b/include/perfetto/ext/base/metatrace_events.h
@@ -29,6 +29,7 @@
   TAG_PROC_POLLERS = 1 << 1,
   TAG_TRACE_WRITER = 1 << 2,
   TAG_TRACE_SERVICE = 1 << 3,
+  TAG_PRODUCER = 1 << 4,
 };
 
 // The macros below generate matching enums and arrays of string literals.
@@ -60,18 +61,31 @@
   F(FTRACE_READ_TICK), \
   F(FTRACE_CPU_READ_CYCLE), \
   F(FTRACE_CPU_READ_BATCH), \
-  F(KALLSYMS_PARSE)
+  F(KALLSYMS_PARSE), \
+  F(PROFILER_READ_TICK), \
+  F(PROFILER_READ_CPU), \
+  F(PROFILER_UNWIND_TICK), \
+  F(PROFILER_UNWIND_SAMPLE), \
+  F(PROFILER_UNWIND_INITIAL_ATTEMPT), \
+  F(PROFILER_UNWIND_ATTEMPT), \
+  F(PROFILER_MAPS_PARSE), \
+  F(PROFILER_MAPS_REPARSE), \
+  F(PROFILER_UNWIND_CACHE_CLEAR)
 
 // Append only, see above.
 //
-// FTRACE_SERVICE_COMMIT_DATA is a bit-packed representation of an event, see
-// tracing_service_impl.cc for the format.
+// Values that aren't used as counters:
+// * FTRACE_SERVICE_COMMIT_DATA is a bit-packed representation of an event, see
+//   tracing_service_impl.cc for the format.
+// * PROFILER_UNWIND_CURRENT_PID represents the PID that is being unwound.
 //
 #define PERFETTO_METATRACE_COUNTERS(F) \
   F(COUNTER_ZERO_UNUSED),\
   F(FTRACE_PAGES_DRAINED), \
   F(PS_PIDS_SCANNED), \
-  F(TRACE_SERVICE_COMMIT_DATA)
+  F(TRACE_SERVICE_COMMIT_DATA), \
+  F(PROFILER_UNWIND_QUEUE_SZ), \
+  F(PROFILER_UNWIND_CURRENT_PID)
 
 // clang-format on
 
diff --git a/include/perfetto/ext/base/string_utils.h b/include/perfetto/ext/base/string_utils.h
index 22c69ee..f1d733c 100644
--- a/include/perfetto/ext/base/string_utils.h
+++ b/include/perfetto/ext/base/string_utils.h
@@ -37,22 +37,29 @@
   return ('a' <= c && c <= 'z') ? static_cast<char>(c + ('A' - 'a')) : c;
 }
 
-inline Optional<uint32_t> CStringToUInt32(const char* s) {
+inline Optional<uint32_t> CStringToUInt32(const char* s, int base = 10) {
   char* endptr = nullptr;
-  uint64_t value = strtoul(s, &endptr, 10);
-  Optional<uint32_t> result(base::nullopt);
-  if (*s != '\0' && *endptr == '\0')
-    result = static_cast<uint32_t>(value);
-  return result;
+  auto value = static_cast<uint32_t>(strtoul(s, &endptr, base));
+  return (*s && !*endptr) ? base::make_optional(value) : base::nullopt;
 }
 
-inline Optional<int32_t> CStringToInt32(const char* s) {
+inline Optional<int32_t> CStringToInt32(const char* s, int base = 10) {
   char* endptr = nullptr;
-  int64_t value = strtol(s, &endptr, 10);
-  Optional<int32_t> result(base::nullopt);
-  if (*s != '\0' && *endptr == '\0')
-    result = static_cast<int32_t>(value);
-  return result;
+  auto value = static_cast<int32_t>(strtol(s, &endptr, base));
+  return (*s && !*endptr) ? base::make_optional(value) : base::nullopt;
+}
+
+// Note: it saturates to 7fffffffffffffff if parsing a hex number >= 0x8000...
+inline Optional<int64_t> CStringToInt64(const char* s, int base = 10) {
+  char* endptr = nullptr;
+  auto value = static_cast<int64_t>(strtoll(s, &endptr, base));
+  return (*s && !*endptr) ? base::make_optional(value) : base::nullopt;
+}
+
+inline Optional<uint64_t> CStringToUInt64(const char* s, int base = 10) {
+  char* endptr = nullptr;
+  auto value = static_cast<uint64_t>(strtoull(s, &endptr, base));
+  return (*s && !*endptr) ? base::make_optional(value) : base::nullopt;
 }
 
 inline Optional<double> CStringToDouble(const char* s) {
@@ -64,12 +71,20 @@
   return result;
 }
 
-inline Optional<uint32_t> StringToUInt32(const std::string& s) {
-  return CStringToUInt32(s.c_str());
+inline Optional<uint32_t> StringToUInt32(const std::string& s, int base = 10) {
+  return CStringToUInt32(s.c_str(), base);
 }
 
-inline Optional<int32_t> StringToInt32(const std::string& s) {
-  return CStringToInt32(s.c_str());
+inline Optional<int32_t> StringToInt32(const std::string& s, int base = 10) {
+  return CStringToInt32(s.c_str(), base);
+}
+
+inline Optional<uint64_t> StringToUInt64(const std::string& s, int base = 10) {
+  return CStringToUInt64(s.c_str(), base);
+}
+
+inline Optional<int64_t> StringToInt64(const std::string& s, int base = 10) {
+  return CStringToInt64(s.c_str(), base);
 }
 
 inline Optional<double> StringToDouble(const std::string& s) {
diff --git a/include/perfetto/ext/base/subprocess.h b/include/perfetto/ext/base/subprocess.h
new file mode 100644
index 0000000..2733152
--- /dev/null
+++ b/include/perfetto/ext/base/subprocess.h
@@ -0,0 +1,222 @@
+/*
+ * 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_EXT_BASE_SUBPROCESS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_SUBPROCESS_H_
+
+#include "perfetto/base/build_config.h"
+
+// This is a #if as opposite to a GN condition, because GN conditions aren't propagated when
+// translating to Bazel or other build systems, as they get resolved at translation time. Without
+// this, the Bazel build breaks on Windows.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
+#define PERFETTO_HAS_SUBPROCESS() 1
+#else
+#define PERFETTO_HAS_SUBPROCESS() 0
+#endif
+
+#include <functional>
+#include <initializer_list>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/base/proc_utils.h"
+#include "perfetto/ext/base/pipe.h"
+#include "perfetto/ext/base/scoped_file.h"
+
+namespace perfetto {
+namespace base {
+
+// Handles creation and lifecycle management of subprocesses, taking care of
+// all subtleties involved in handling processes on UNIX.
+// This class allows to deal with macro two use-cases:
+// 1) fork() + exec() equivalent: for spawning a brand new process image.
+//    This happens when |args.exec_cmd| is not empty.
+//    This is safe to use even in a multi-threaded environment.
+// 2) fork(): for spawning a process and running a function.
+//    This happens when |args.entrypoint_for_testing| is not empty.
+//    This is intended only for tests as it is extremely subtle.
+//    This mode must be used with extreme care. Before the entrypoint is
+//    invoked all file descriptors other than stdin/out/err and the ones
+//    specified in |args.preserve_fds| will be closed, to avoid each process
+//    retaining a dupe of other subprocesses pipes. This however means that
+//    any non trivial calls (including logging) must be avoided as they might
+//    refer to FDs that are now closed. The entrypoint should really be used
+//    just to signal a pipe or similar for synchronizing sequencing in tests.
+
+//
+// This class allows to control stdin/out/err pipe redirection and takes care
+// of keeping all the pipes pumped (stdin) / drained (stdout/err), in a similar
+// fashion of python's subprocess.Communicate()
+// stdin: is always piped and closed once the |args.input| buffer is written.
+// stdout/err can be either:
+//   - dup()ed onto the parent process stdout/err.
+//   - redirected onto /dev/null.
+//   - piped onto a buffer (see output() method). There is only one output
+//     buffer in total. If both stdout and stderr are set to kBuffer mode, they
+//     will be merged onto the same. There doesn't seem any use case where they
+//     are needed distinctly.
+//
+// Some caveats worth mentioning:
+// - It always waitpid()s, to avoid leaving zombies around. If the process is
+//   not terminated by the time the destructor is reached, the dtor will
+//   send a SIGKILL and wait for the termination.
+// - After fork()-ing it will close all file descriptors, preserving only
+//   stdin/out/err and the fds listed in |args.preserve_fds|.
+// - On Linux/Android, the child process will be SIGKILL-ed if the calling
+//   thread exists, even if the Subprocess is std::move()-d onto another thread.
+//   This happens by virtue PR_SET_PDEATHSIG, which is used to avoid that
+//   child processes are leaked in the case of a crash of the parent (frequent
+//   in tests). However, the child process might still be leaked if execing
+//   a setuid/setgid binary (see man 2 prctl).
+//
+// Usage:
+// base::Subprocess p({"/bin/cat", "-"});
+// (or equivalently:
+//     base::Subprocess p;
+//     p.args.exec_cmd.push_back("/bin/cat");
+//     p.args.exec_cmd.push_back("-");
+//  )
+// p.args.stdout_mode = base::Subprocess::kBuffer;
+// p.args.stderr_mode = base::Subprocess::kInherit;
+// p.args.input = "stdin contents";
+// p.Call();
+// (or equivalently:
+//     p.Start();
+//     p.Wait();
+// )
+// EXPECT_EQ(p.status(), base::Subprocess::kExited);
+// EXPECT_EQ(p.returncode(), 0);
+class Subprocess {
+ public:
+  enum Status {
+    kNotStarted = 0,  // Before calling Start() or Call().
+    kRunning,         // After calling Start(), before Wait().
+    kExited,          // The subprocess exited (either succesully or not).
+    kKilledBySignal,  // The subprocess has been killed by a signal.
+  };
+
+  enum OutputMode {
+    kInherit = 0,  // Inherit's the caller process stdout/stderr.
+    kDevNull,      // dup() onto /dev/null
+    kBuffer        // dup() onto a pipe and move it into the output() buffer.
+  };
+
+  // Input arguments for configuring the subprocess behavior.
+  struct Args {
+    Args(std::initializer_list<std::string> _cmd = {}) : exec_cmd(_cmd) {}
+    Args(Args&&) noexcept;
+    Args& operator=(Args&&);
+    // If non-empty this will cause an exec() when Start()/Call() are called.
+    std::vector<std::string> exec_cmd;
+
+    // If non-empty, it changes the argv[0] argument passed to exec. If
+    // unset, argv[0] == exec_cmd[0]. This is to handle cases like:
+    // exec_cmd = {"/proc/self/exec"}, argv0: "my_custom_test_override".
+    std::string argv0_override;
+
+    // If non-empty this will be invoked on the fork()-ed child process, after
+    // stdin/out/err has been redirected and all other file descriptor are
+    // closed.
+    // It is valid to specify both |exec_cmd| AND |entrypoint_for_testing|.
+    // In this case |entrypoint_for_testing| will be invoked just before the
+    // exec() call, but after having closed all fds % stdin/out/err.
+    // This is for synchronization barriers in tests.
+    std::function<void()> entrypoint_for_testing;
+
+    // If non-empty, replaces the environment passed to exec().
+    std::vector<std::string> env;
+
+    // The file descriptors in this list will not be closed.
+    std::vector<int> preserve_fds;
+
+    // The data to push in the child process stdin.
+    std::string input;
+
+    OutputMode stdout_mode = kInherit;
+    OutputMode stderr_mode = kInherit;
+
+    // Returns " ".join(exec_cmd), quoting arguments.
+    std::string GetCmdString() const;
+  };
+
+  explicit Subprocess(std::initializer_list<std::string> exec_cmd = {});
+  Subprocess(Subprocess&&) noexcept;
+  Subprocess& operator=(Subprocess&&);
+  ~Subprocess();  // It will KillAndWaitForTermination() if still alive.
+
+  // Starts the subprocess but doesn't wait for its termination. The caller
+  // is expected to either call Wait() or Poll() after this call.
+  void Start();
+
+  // Wait for process termination. Can be called more than once.
+  // Args:
+  //   |timeout_ms| = 0: wait indefinitely.
+  //   |timeout_ms| > 0: wait for at most |timeout_ms|.
+  // Returns:
+  //  True: The process terminated. See status() and returncode().
+  //  False: Timeout reached, the process is still running. In this case the
+  //         process will be left in the kRunning state.
+  bool Wait(int timeout_ms = 0);
+
+  // Equivalent of Start() + Wait();
+  // Returns true if the process exited cleanly with return code 0. False in
+  // any othe case.
+  bool Call(int timeout_ms = 0);
+
+  Status Poll();
+
+  // Sends a SIGKILL and wait to see the process termination.
+  void KillAndWaitForTermination();
+
+  PlatformProcessId pid() const { return pid_; }
+  Status status() const { return status_; }
+  int returncode() const { return returncode_; }
+
+  // This contains both stdout and stderr (if the corresponding _mode ==
+  // kBuffer). It's non-const so the caller can std::move() it.
+  std::string& output() { return output_; }
+
+  Args args;
+
+ private:
+  Subprocess(const Subprocess&) = delete;
+  Subprocess& operator=(const Subprocess&) = delete;
+  void TryPushStdin();
+  void TryReadStdoutAndErr();
+  void TryReadExitStatus();
+  void KillAtMostOnce();
+  bool PollInternal(int poll_timeout_ms);
+
+  base::Pipe stdin_pipe_;
+  base::Pipe stdouterr_pipe_;
+  base::Pipe exit_status_pipe_;
+  PlatformProcessId pid_;
+  size_t input_written_ = 0;
+  Status status_ = kNotStarted;
+  int returncode_ = -1;
+  std::string output_;  // Stdin+stderr. Only when kBuffer.
+  std::thread waitpid_thread_;
+};
+
+}  // namespace base
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_SUBPROCESS_H_
diff --git a/include/perfetto/ext/base/thread_utils.h b/include/perfetto/ext/base/thread_utils.h
new file mode 100644
index 0000000..7869e77
--- /dev/null
+++ b/include/perfetto/ext/base/thread_utils.h
@@ -0,0 +1,61 @@
+/*
+ * 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_EXT_BASE_THREAD_UTILS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_THREAD_UTILS_H_
+
+#include <string>
+
+#include "perfetto/base/build_config.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
+#include <pthread.h>
+#include <string.h>
+#include <algorithm>
+#endif
+
+// Internal implementation utils that aren't as widely useful/supported as
+// base/thread_utils.h.
+
+namespace perfetto {
+namespace base {
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
+// Sets the "comm" of the calling thread to the first 15 chars of the given
+// string.
+inline bool MaybeSetThreadName(const std::string& name) {
+  char buf[16] = {};
+  size_t sz = std::min(name.size(), static_cast<size_t>(15));
+  strncpy(buf, name.c_str(), sz);
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
+  return pthread_setname_np(buf) == 0;
+#else
+  return pthread_setname_np(pthread_self(), buf) == 0;
+#endif
+}
+#else
+inline void MaybeSetThreadName(const std::string&) {}
+#endif
+
+}  // namespace base
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_THREAD_UTILS_H_
diff --git a/include/perfetto/ext/base/unix_socket.h b/include/perfetto/ext/base/unix_socket.h
index 07e0f58..df0cad8 100644
--- a/include/perfetto/ext/base/unix_socket.h
+++ b/include/perfetto/ext/base/unix_socket.h
@@ -181,8 +181,6 @@
     kListening    // After Listen(), until Shutdown().
   };
 
-  enum class BlockingMode { kNonBlocking, kBlocking };
-
   // Creates a socket and starts listening. If SockFamily::kUnix and
   // |socket_name| starts with a '@', an abstract UNIX dmoain socket will be
   // created instead of a filesystem-linked UNIX socket (Linux/Android only).
@@ -242,26 +240,16 @@
   // EventListener::OnDisconnect() will be called.
   // If the socket is not connected, Send() will just return false.
   // Does not append a null string terminator to msg in any case.
-  //
-  // DO NOT PASS kNonBlocking, it is broken.
-  bool Send(const void* msg,
-            size_t len,
-            const int* send_fds,
-            size_t num_fds,
-            BlockingMode blocking = BlockingMode::kNonBlocking);
+  bool Send(const void* msg, size_t len, const int* send_fds, size_t num_fds);
 
-  inline bool Send(const void* msg,
-                   size_t len,
-                   int send_fd = -1,
-                   BlockingMode blocking = BlockingMode::kNonBlocking) {
+  inline bool Send(const void* msg, size_t len, int send_fd = -1) {
     if (send_fd != -1)
-      return Send(msg, len, &send_fd, 1, blocking);
-    return Send(msg, len, nullptr, 0, blocking);
+      return Send(msg, len, &send_fd, 1);
+    return Send(msg, len, nullptr, 0);
   }
 
-  inline bool Send(const std::string& msg,
-                   BlockingMode blocking = BlockingMode::kNonBlocking) {
-    return Send(msg.c_str(), msg.size() + 1, -1, blocking);
+  inline bool Send(const std::string& msg) {
+    return Send(msg.c_str(), msg.size() + 1, -1);
   }
 
   // Returns the number of bytes (<= |len|) written in |msg| or 0 if there
diff --git a/include/perfetto/ext/base/watchdog_posix.h b/include/perfetto/ext/base/watchdog_posix.h
index 40e3289..9a6e1dd 100644
--- a/include/perfetto/ext/base/watchdog_posix.h
+++ b/include/perfetto/ext/base/watchdog_posix.h
@@ -27,6 +27,14 @@
 namespace perfetto {
 namespace base {
 
+struct ProcStat {
+  unsigned long int utime = 0l;
+  unsigned long int stime = 0l;
+  long int rss_pages = -1l;
+};
+
+bool ReadProcStat(int fd, ProcStat* out);
+
 // Ensures that the calling program does not exceed certain hard limits on
 // resource usage e.g. time, memory and CPU. If exceeded, the program is
 // crashed.
diff --git a/include/perfetto/ext/base/weak_ptr.h b/include/perfetto/ext/base/weak_ptr.h
index 7ba4ca3..778c4dd 100644
--- a/include/perfetto/ext/base/weak_ptr.h
+++ b/include/perfetto/ext/base/weak_ptr.h
@@ -90,9 +90,23 @@
 
   // Can be safely called on any thread, since it simply copies |weak_ptr_|.
   // Note that any accesses to the returned pointer need to be made on the
-  // thread that created the factory.
+  // thread that created/reset the factory.
   WeakPtr<T> GetWeakPtr() const { return weak_ptr_; }
 
+  // Reset the factory to a new owner & thread. May only be called before any
+  // weak pointers were passed out. Future weak pointers will be valid on the
+  // calling thread.
+  void Reset(T* owner) {
+    // Reset thread checker to current thread.
+    PERFETTO_DETACH_FROM_THREAD(thread_checker);
+    PERFETTO_DCHECK_THREAD(thread_checker);
+
+    // We should not have passed out any weak pointers yet at this point.
+    PERFETTO_DCHECK(weak_ptr_.handle_.use_count() == 1);
+
+    weak_ptr_ = WeakPtr<T>(std::shared_ptr<T*>(new T* {owner}));
+  }
+
  private:
   WeakPtrFactory(const WeakPtrFactory&) = delete;
   WeakPtrFactory& operator=(const WeakPtrFactory&) = delete;
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/BUILD.gn b/include/perfetto/ext/tracing/core/BUILD.gn
index 69eff99..89e75d4 100644
--- a/include/perfetto/ext/tracing/core/BUILD.gn
+++ b/include/perfetto/ext/tracing/core/BUILD.gn
@@ -28,8 +28,6 @@
     "shared_memory_abi.h",
     "shared_memory_arbiter.h",
     "slice.h",
-    "startup_trace_writer.h",
-    "startup_trace_writer_registry.h",
     "trace_packet.h",
     "trace_stats.h",
     "trace_writer.h",
diff --git a/include/perfetto/ext/tracing/core/basic_types.h b/include/perfetto/ext/tracing/core/basic_types.h
index df68632..99005ce 100644
--- a/include/perfetto/ext/tracing/core/basic_types.h
+++ b/include/perfetto/ext/tracing/core/basic_types.h
@@ -59,6 +59,13 @@
 // Unique within the scope of the tracing service.
 using BufferID = uint16_t;
 
+// Target buffer ID for SharedMemoryArbiter. Values up to max uint16_t are
+// equivalent to a bound BufferID. Values above max uint16_t are reservation IDs
+// for the target buffer of a startup trace writer. Reservation IDs will be
+// translated to actual BufferIDs after they are bound by
+// SharedMemoryArbiter::BindStartupTargetBuffer().
+using MaybeUnboundBufferID = uint32_t;
+
 // Keep this in sync with SharedMemoryABI::PageHeader::target_buffer.
 static constexpr BufferID kMaxTraceBufferID = static_cast<BufferID>(-1);
 
diff --git a/include/perfetto/ext/tracing/core/shared_memory.h b/include/perfetto/ext/tracing/core/shared_memory.h
index 3c77e07..11d078a 100644
--- a/include/perfetto/ext/tracing/core/shared_memory.h
+++ b/include/perfetto/ext/tracing/core/shared_memory.h
@@ -46,6 +46,7 @@
 
   virtual void* start() const = 0;
   virtual size_t size() const = 0;
+  virtual int fd() const = 0;
 };
 
 }  // namespace perfetto
diff --git a/include/perfetto/ext/tracing/core/shared_memory_arbiter.h b/include/perfetto/ext/tracing/core/shared_memory_arbiter.h
index 0937621..69849bb 100644
--- a/include/perfetto/ext/tracing/core/shared_memory_arbiter.h
+++ b/include/perfetto/ext/tracing/core/shared_memory_arbiter.h
@@ -34,8 +34,6 @@
 class TaskRunner;
 }
 
-class StartupTraceWriter;
-class StartupTraceWriterRegistry;
 class SharedMemory;
 class TraceWriter;
 
@@ -48,41 +46,74 @@
   // Creates a new TraceWriter and assigns it a new WriterID. The WriterID is
   // written in each chunk header owned by a given TraceWriter and is used by
   // the Service to reconstruct TracePackets written by the same TraceWriter.
-  // Returns null impl of TraceWriter if all WriterID slots are exhausted.
+  // Returns null impl of TraceWriter if all WriterID slots are exhausted. The
+  // writer will commit to the provided |target_buffer|. If the arbiter was
+  // created via CreateUnbound(), only BufferExhaustedPolicy::kDrop is
+  // supported.
   virtual std::unique_ptr<TraceWriter> CreateTraceWriter(
       BufferID target_buffer,
       BufferExhaustedPolicy buffer_exhausted_policy =
           BufferExhaustedPolicy::kDefault) = 0;
 
-  // Binds the provided unbound StartupTraceWriterRegistry to the arbiter's SMB.
-  // Normally this happens when the perfetto service has been initialized and we
-  // want to rebind all the writers created in the early startup phase.
+  // Creates a TraceWriter that will commit to the target buffer with the given
+  // reservation ID (creating a new reservation for this ID if none exists yet).
+  // The buffer reservation should be bound to an actual BufferID via
+  // BindStartupTargetBuffer() once the actual BufferID is known. Only supported
+  // if the arbiter was created using CreateUnbound(), and may be called while
+  // the arbiter is unbound.
   //
-  // All StartupTraceWriters created by the registry are bound to the arbiter
-  // and the given target buffer. The writers may not be bound immediately if
-  // they are concurrently being written to or if this method isn't called on
-  // the arbiter's TaskRunner. The registry will retry on the arbiter's
-  // TaskRunner until all writers were bound successfully.
+  // While any unbound buffer reservation exists, all commits will be buffered
+  // until all reservations were bound. Thus, until all reservations are bound,
+  // the data written to the SMB will not be consumed by the service - the SMB
+  // size should be chosen with this in mind. Startup writers always use
+  // BufferExhaustedPolicy::kDrop, as we cannot feasibly stall while not
+  // flushing to the service.
   //
-  // The commit of the StartupTraceWriters' locally buffered data to the SMB is
-  // rate limited to avoid exhausting the SMB, and may continue asynchronously
-  // even after all writers were bound.
-  //
-  // By calling this method, the registry's ownership is transferred to the
-  // arbiter. The arbiter will delete the registry once all writers were bound.
-  //
-  // TODO(eseckler): Make target buffer assignment more flexible (i.e. per
-  // writer). For now, embedders can use multiple registries instead.
-  virtual void BindStartupTraceWriterRegistry(
-      std::unique_ptr<StartupTraceWriterRegistry>,
-      BufferID target_buffer) = 0;
+  // The |target_buffer_reservation_id| should be greater than 0 but can
+  // otherwise be freely chosen by the producer and is only used to translate
+  // packets into the actual buffer id once
+  // BindStartupTargetBuffer(reservation_id) is called. For example, Chrome uses
+  // startup tracing not only for the first, but also subsequent tracing
+  // sessions (to enable tracing in the browser process before it instructs the
+  // tracing service to start tracing asynchronously, minimizing trace data loss
+  // in the meantime), and increments the reservation ID between sessions.
+  // Similarly, if more than a single target buffer per session is required
+  // (e.g. for two different data sources), different reservation IDs should be
+  // chosen for different targets buffers.
+  virtual std::unique_ptr<TraceWriter> CreateStartupTraceWriter(
+      uint16_t target_buffer_reservation_id) = 0;
+
+  // Should only be called on unbound SharedMemoryArbiters. Binds the arbiter to
+  // the provided ProducerEndpoint and TaskRunner. Should be called only once
+  // and on the provided |TaskRunner|. Usually called by the producer (i.e., no
+  // specific data source) once it connects to the service. Both the endpoint
+  // and task runner should remain valid for the remainder of the arbiter's
+  // lifetime.
+  virtual void BindToProducerEndpoint(TracingService::ProducerEndpoint*,
+                                      base::TaskRunner*) = 0;
+
+  // Binds commits from TraceWriters created via CreateStartupTraceWriter() with
+  // the given |target_buffer_reservation_id| to |target_buffer_id|. May only be
+  // called once per |target_buffer_reservation_id|. Should be called on the
+  // arbiter's TaskRunner, and after BindToProducerEndpoint() was called.
+  // Usually, it is called by a specific data source, after it received its
+  // configuration (including the target buffer ID) from the service.
+  virtual void BindStartupTargetBuffer(uint16_t target_buffer_reservation_id,
+                                       BufferID target_buffer_id) = 0;
+
+  // Treat the reservation as resolved to an invalid buffer. Commits for this
+  // reservation will be flushed to the service ASAP. The service will free
+  // committed chunks but otherwise ignore them. The producer can call this
+  // method, for example, if connection to the tracing service failed or the
+  // session was stopped concurrently before the connection was established.
+  virtual void AbortStartupTracingForReservation(
+      uint16_t target_buffer_reservation_id) = 0;
 
   // Notifies the service that all data for the given FlushRequestID has been
-  // committed in the shared memory buffer.
+  // committed in the shared memory buffer. Should only be called while bound.
   virtual void NotifyFlushComplete(FlushRequestID) = 0;
 
-  // Implemented in src/core/shared_memory_arbiter_impl.cc.
-  // Args:
+  // Create a bound arbiter instance. Args:
   // |SharedMemory|: the shared memory buffer to use.
   // |page_size|: a multiple of 4KB that defines the granularity of tracing
   // pages. See tradeoff considerations in shared_memory_abi.h.
@@ -90,11 +121,34 @@
   // chunks and register trace writers.
   // |TaskRunner|: Task runner for perfetto's main thread, which executes the
   // OnPagesCompleteCallback and IPC calls to the |ProducerEndpoint|.
+  //
+  // Implemented in src/core/shared_memory_arbiter_impl.cc.
   static std::unique_ptr<SharedMemoryArbiter> CreateInstance(
       SharedMemory*,
       size_t page_size,
       TracingService::ProducerEndpoint*,
       base::TaskRunner*);
+
+  // Create an unbound arbiter instance, which should later be bound to a
+  // ProducerEndpoint and TaskRunner by calling BindToProducerEndpoint(). The
+  // returned arbiter will ONLY support trace writers with
+  // BufferExhaustedPolicy::kDrop.
+  //
+  // An unbound SharedMemoryArbiter can be used to write to a producer-created
+  // SharedMemory buffer before the producer connects to the tracing service.
+  // The producer can then pass this SMB to the service when it connects (see
+  // TracingService::ConnectProducer).
+  //
+  // To trace into the SMB before the service starts the tracing session, trace
+  // writers can be obtained via CreateStartupTraceWriter() and later associated
+  // with a target buffer via BindStartupTargetBuffer(), once the target buffer
+  // is known.
+  //
+  // Implemented in src/core/shared_memory_arbiter_impl.cc. See CreateInstance()
+  // for comments about the arguments.
+  static std::unique_ptr<SharedMemoryArbiter> CreateUnboundInstance(
+      SharedMemory*,
+      size_t page_size);
 };
 
 }  // namespace perfetto
diff --git a/include/perfetto/ext/tracing/core/startup_trace_writer.h b/include/perfetto/ext/tracing/core/startup_trace_writer.h
deleted file mode 100644
index 021eb35..0000000
--- a/include/perfetto/ext/tracing/core/startup_trace_writer.h
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * 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_EXT_TRACING_CORE_STARTUP_TRACE_WRITER_H_
-#define INCLUDE_PERFETTO_EXT_TRACING_CORE_STARTUP_TRACE_WRITER_H_
-
-#include <memory>
-#include <mutex>
-#include <set>
-#include <vector>
-
-#include "perfetto/base/export.h"
-#include "perfetto/ext/base/optional.h"
-#include "perfetto/ext/base/thread_checker.h"
-#include "perfetto/ext/tracing/core/basic_types.h"
-#include "perfetto/ext/tracing/core/shared_memory_abi.h"
-#include "perfetto/ext/tracing/core/shared_memory_arbiter.h"
-#include "perfetto/ext/tracing/core/trace_writer.h"
-#include "perfetto/protozero/message_handle.h"
-#include "perfetto/protozero/scattered_heap_buffer.h"
-#include "perfetto/protozero/scattered_stream_writer.h"
-#include "perfetto/tracing/buffer_exhausted_policy.h"
-
-namespace perfetto {
-
-class SharedMemoryArbiterImpl;
-class StartupTraceWriterRegistryHandle;
-
-namespace protos {
-namespace pbzero {
-class TracePacket;
-}  // namespace pbzero
-}  // namespace protos
-
-// Facilitates writing trace events in early phases of an application's startup
-// when the perfetto service is not available yet.
-//
-// Until the service is available, producer threads instantiate an unbound
-// StartupTraceWriter instance (via a StartupTraceWriterRegistry) and use it to
-// emit trace events. Each writer will record the serialized trace events into a
-// temporary local memory buffer.
-//
-// Once the service is available, the producer binds each StartupTraceWriter to
-// the SMB by calling SharedMemoryArbiter::BindStartupTraceWriter(). The data in
-// the writer's local buffer will then be copied into the SMB and the any future
-// writes will proxy directly to a new SMB-backed TraceWriter.
-//
-// Writing to the temporary local trace buffer is guarded by a lock and flag to
-// allow binding the writer from a different thread. When the writer starts
-// writing data by calling NewTracePacket(), the writer thread acquires the lock
-// to set a flag indicating that a write is in progress. Once the packet is
-// finalized, the flag is reset. To bind the writer, the lock is acquired while
-// the flag is unset and released only once binding completed, thereby blocking
-// the writer thread from starting a write concurrently.
-//
-// While unbound, the writer thread should finalize each TracePacket as soon as
-// possible to ensure that it doesn't block binding the writer.
-class PERFETTO_EXPORT StartupTraceWriter
-    : public TraceWriter,
-      public protozero::MessageHandleBase::FinalizationListener {
- public:
-  // Create a StartupTraceWriter bound to |trace_writer|. Should only be called
-  // on the writer thread.
-  explicit StartupTraceWriter(std::unique_ptr<TraceWriter> trace_writer);
-
-  ~StartupTraceWriter() override;
-
-  // Return the given writer back to its registry if it is associated with a
-  // registry and the registry was not yet deleted. Otherwise, the writer is
-  // destroyed synchronously.
-  //
-  // Usually called when the writer's thread is destroyed. Can be called on any
-  // thread. The passed writer may still be unbound or already be bound. If
-  // unbound, the registry will ensure that it is bound before destroying it,
-  // keeping it alive until the registry is bound if necessary. This way, its
-  // buffered data is retained.
-  //
-  // All packets written to the passed writer should have been completed and it
-  // should no longer be used to write data after calling this method.
-  static void ReturnToRegistry(std::unique_ptr<StartupTraceWriter> writer);
-
-  // TraceWriter implementation. These methods should only be called on the
-  // writer thread.
-  TracePacketHandle NewTracePacket() override;
-  void Flush(std::function<void()> callback = {}) override;
-
-  // Note that this will return 0 until the first TracePacket was started after
-  // binding.
-  WriterID writer_id() const override;
-
-  uint64_t written() const override;
-
-  // Returns |true| if the writer thread has observed that the writer was bound
-  // to an SMB. Should only be called on the writer thread.
-  //
-  // The writer thread can use the return value to determine whether it needs to
-  // finalize the current TracePacket as soon as possible. It is only safe for
-  // the writer to batch data into a single TracePacket over a longer time
-  // period when this returns |true|.
-  bool was_bound() const {
-    PERFETTO_DCHECK_THREAD(writer_thread_checker_);
-    return was_bound_;
-  }
-
-  // Should only be called on the writer thread.
-  size_t used_buffer_size();
-
- private:
-  friend class StartupTraceWriterRegistry;
-  friend class StartupTraceWriterTest;
-
-  // Create an unbound StartupTraceWriter associated with the registry pointed
-  // to by the handle. The writer can later be bound by calling
-  // BindToTraceWriter(). The registry handle may be nullptr in tests.
-  StartupTraceWriter(std::shared_ptr<StartupTraceWriterRegistryHandle>,
-                     BufferExhaustedPolicy,
-                     size_t max_buffer_size_bytes);
-
-  StartupTraceWriter(const StartupTraceWriter&) = delete;
-  StartupTraceWriter& operator=(const StartupTraceWriter&) = delete;
-
-  // Bind this StartupTraceWriter to the provided SharedMemoryArbiterImpl.
-  // Called by StartupTraceWriterRegistry::BindToArbiter().
-  //
-  // This method should be called on the arbiter's task runner. If any data was
-  // written locally before the writer was bound, BindToArbiter() will copy this
-  // data into chunks in the provided target buffer via the SMB. The commit of
-  // this data to the SMB is rate limited to avoid exhausting the SMB
-  // (|chunks_per_batch|), and may continue asynchronously on the arbiter's task
-  // runner even if binding was successful, provided the SharedMemoryArbiterImpl
-  // is not destroyed. Passing value 0 for |chunks_per_batch| disables rate
-  // limiting. Any future packets will be directly written into the SMB via a
-  // newly obtained TraceWriter from the arbiter.
-  //
-  // Will fail and return |false| if a concurrent write is in progress. Returns
-  // |true| if successfully bound and should then not be called again.
-  bool BindToArbiter(SharedMemoryArbiterImpl*,
-                     BufferID target_buffer,
-                     size_t chunks_per_batch) PERFETTO_WARN_UNUSED_RESULT;
-
-  // protozero::MessageHandleBase::FinalizationListener implementation.
-  void OnMessageFinalized(protozero::Message* message) override;
-
-  void OnTracePacketCompleted();
-  ChunkID CommitLocalBufferChunks(SharedMemoryArbiterImpl*,
-                                  WriterID,
-                                  BufferID,
-                                  size_t chunks_per_batch,
-                                  SharedMemoryABI::Chunk first_chunk);
-
-  PERFETTO_THREAD_CHECKER(writer_thread_checker_)
-
-  std::shared_ptr<StartupTraceWriterRegistryHandle> registry_handle_;
-
-  // Only set and accessed from the writer thread. The writer thread flips this
-  // bit when it sees that trace_writer_ is set (while holding the lock).
-  // Caching this fact in this variable avoids the need to acquire the lock to
-  // check on later calls to NewTracePacket().
-  bool was_bound_ = false;
-
-  const BufferExhaustedPolicy buffer_exhausted_policy_ =
-      BufferExhaustedPolicy::kDefault;
-  const size_t max_buffer_size_bytes_ = 0;
-
-  // Only accessed on the writer thread.
-  std::unique_ptr<TraceWriter> null_trace_writer_ = nullptr;
-
-  // All variables below this point are protected by |lock_|.
-  std::mutex lock_;
-
-  // Never reset once it is changed from |nullptr|.
-  std::unique_ptr<TraceWriter> trace_writer_ = nullptr;
-
-  // Local memory buffer for trace packets written before the writer is bound.
-  std::unique_ptr<protozero::ScatteredHeapBuffer> memory_buffer_;
-  std::unique_ptr<protozero::ScatteredStreamWriter> memory_stream_writer_;
-
-  std::unique_ptr<std::vector<uint32_t>> packet_sizes_;
-
-  // Whether the writer thread is currently writing a TracePacket.
-  bool write_in_progress_ = false;
-
-  // The packet returned via NewTracePacket() while the writer is unbound. Reset
-  // to |nullptr| once bound. Owned by this class, TracePacketHandle has just a
-  // pointer to it.
-  std::unique_ptr<protos::pbzero::TracePacket> cur_packet_;
-};
-
-}  // namespace perfetto
-
-#endif  // INCLUDE_PERFETTO_EXT_TRACING_CORE_STARTUP_TRACE_WRITER_H_
diff --git a/include/perfetto/ext/tracing/core/startup_trace_writer_registry.h b/include/perfetto/ext/tracing/core/startup_trace_writer_registry.h
deleted file mode 100644
index 0896f32..0000000
--- a/include/perfetto/ext/tracing/core/startup_trace_writer_registry.h
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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_EXT_TRACING_CORE_STARTUP_TRACE_WRITER_REGISTRY_H_
-#define INCLUDE_PERFETTO_EXT_TRACING_CORE_STARTUP_TRACE_WRITER_REGISTRY_H_
-
-#include <functional>
-#include <memory>
-#include <mutex>
-#include <vector>
-
-#include "perfetto/base/export.h"
-#include "perfetto/ext/base/weak_ptr.h"
-#include "perfetto/ext/tracing/core/basic_types.h"
-#include "perfetto/ext/tracing/core/shared_memory_arbiter.h"
-#include "perfetto/tracing/buffer_exhausted_policy.h"
-
-namespace perfetto {
-
-class SharedMemoryArbiterImpl;
-class StartupTraceWriter;
-class StartupTraceWriterRegistry;
-
-namespace base {
-class TaskRunner;
-}  // namespace base
-
-// Used to return a StartupTraceWriter back to its registry when the writer's
-// thread is destroyed, provided the registry itself wasn't deleted yet. The
-// indirection via the handle is necessary to avoid potential deadlocks caused
-// by lock order inversion. These issues are avoided by locking on the handle's
-// common lock.
-class StartupTraceWriterRegistryHandle {
- public:
-  explicit StartupTraceWriterRegistryHandle(StartupTraceWriterRegistry*);
-
-  // Called by StartupTraceWriterRegistry destructor.
-  void OnRegistryDestroyed();
-
-  // Called by StartupTraceWriter::ReturnToRegistry.
-  void ReturnWriterToRegistry(std::unique_ptr<StartupTraceWriter> writer);
-
- private:
-  StartupTraceWriterRegistryHandle(const StartupTraceWriterRegistryHandle&) =
-      delete;
-  StartupTraceWriterRegistryHandle& operator=(
-      const StartupTraceWriterRegistryHandle&) = delete;
-
-  std::mutex lock_;
-  StartupTraceWriterRegistry* registry_;
-};
-
-// Embedders can use this registry to create unbound StartupTraceWriters during
-// startup, and later bind them all safely to an arbiter and target buffer.
-class PERFETTO_EXPORT StartupTraceWriterRegistry {
- public:
-  StartupTraceWriterRegistry();
-  ~StartupTraceWriterRegistry();
-
-  // Buffer size defaults to 1 mB per writer.
-  static constexpr size_t kDefaultMaxBufferSizeBytes = 1024 * 1024;
-
-  // Returns a new unbound StartupTraceWriter. Should only be called while
-  // unbound. Usually called on a writer thread. The writer should never be
-  // destroyed by the caller directly, but instead returned to the registry by
-  // calling StartupTraceWriter::ReturnToRegistry.
-  std::unique_ptr<StartupTraceWriter> CreateUnboundTraceWriter(
-      BufferExhaustedPolicy = BufferExhaustedPolicy::kDefault,
-      size_t max_buffer_size_bytes = kDefaultMaxBufferSizeBytes);
-
-  // Binds all StartupTraceWriters created by this registry to the given arbiter
-  // and target buffer. Should only be called once and on the passed
-  // TaskRunner's sequence. See
-  // SharedMemoryArbiter::BindStartupTraceWriterRegistry() for details.
-  //
-  // Note that the writers may not be bound synchronously if they are
-  // concurrently being written to. The registry will retry on the passed
-  // TaskRunner until all writers were bound successfully.
-  //
-  // Calls |on_bound_callback| asynchronously on the passed TaskRunner once all
-  // writers were bound.
-  //
-  // The commit of the StartupTraceWriters' locally buffered data to the SMB is
-  // rate limited to avoid exhausting the SMB, and may continue asynchronously
-  // even after |on_bound_callback| was called.
-  void BindToArbiter(
-      SharedMemoryArbiterImpl*,
-      BufferID target_buffer,
-      base::TaskRunner*,
-      std::function<void(StartupTraceWriterRegistry*)> on_bound_callback);
-
- private:
-  friend class StartupTraceWriterRegistryHandle;
-  friend class StartupTraceWriterTest;
-
-  StartupTraceWriterRegistry(const StartupTraceWriterRegistry&) = delete;
-  StartupTraceWriterRegistry& operator=(const StartupTraceWriterRegistry&) =
-      delete;
-
-  // Try to bind the remaining unbound writers and post a continuation to
-  // |task_runner_| if any writers could not be bound.
-  void TryBindWriters();
-
-  // Notifies the arbiter when we have bound all writers. May delete |this|.
-  void OnUnboundWritersRemovedLocked();
-
-  // Return a StartupTraceWriter back to the registry, see
-  // StartupTraceWriter::ReturnToRegistry.
-  void ReturnTraceWriter(std::unique_ptr<StartupTraceWriter>);
-
-  std::shared_ptr<StartupTraceWriterRegistryHandle> handle_;
-
-  // Begin lock-protected members.
-  std::mutex lock_;
-
-  // Unbound writers that we handed out to writer threads. These writers may be
-  // concurrently written to by the writer threads.
-  std::vector<StartupTraceWriter*> unbound_writers_;
-
-  // Unbound writers that writer threads returned to the registry by calling
-  // ReturnUnboundTraceWriter(). Writers are removed from |unbound_writers_|
-  // when they are added to |unbound_owned_writers_|. No new data can be written
-  // to these writers.
-  std::vector<std::unique_ptr<StartupTraceWriter>> unbound_owned_writers_;
-
-  SharedMemoryArbiterImpl* arbiter_ = nullptr;  // |nullptr| while unbound.
-  BufferID target_buffer_ = 0;
-  base::TaskRunner* task_runner_;
-  size_t chunks_per_batch_ = 0;
-  std::function<void(StartupTraceWriterRegistry*)> on_bound_callback_ = nullptr;
-
-  // Keep at the end. Initialized during |BindToArbiter()|, like |task_runner_|.
-  // Weak pointers are only valid on |task_runner_|'s thread/sequence.
-  std::unique_ptr<base::WeakPtrFactory<StartupTraceWriterRegistry>>
-      weak_ptr_factory_;
-  // End lock-protected members.
-};
-
-}  // namespace perfetto
-
-#endif  // INCLUDE_PERFETTO_EXT_TRACING_CORE_STARTUP_TRACE_WRITER_REGISTRY_H_
diff --git a/include/perfetto/ext/tracing/core/trace_writer.h b/include/perfetto/ext/tracing/core/trace_writer.h
index 99662b2..6baaad9 100644
--- a/include/perfetto/ext/tracing/core/trace_writer.h
+++ b/include/perfetto/ext/tracing/core/trace_writer.h
@@ -79,14 +79,7 @@
   virtual WriterID writer_id() const = 0;
 
   // Bytes written since creation. Is not reset when new chunks are acquired.
-  virtual uint64_t written() const = 0;
-
-  // Set the id of the first chunk the writer will emit. Returns |false| if not
-  // implemented or if the first chunk was already emitted by the writer.
-  //
-  // StartupTraceWriter will call this if it committed buffered data on
-  // behalf of the TraceWriter.
-  virtual bool SetFirstChunkId(ChunkID);
+  virtual uint64_t written() const override = 0;
 
  private:
   TraceWriter(const TraceWriter&) = delete;
diff --git a/include/perfetto/ext/tracing/core/tracing_service.h b/include/perfetto/ext/tracing/core/tracing_service.h
index c097a6e..8715ad6 100644
--- a/include/perfetto/ext/tracing/core/tracing_service.h
+++ b/include/perfetto/ext/tracing/core/tracing_service.h
@@ -106,11 +106,18 @@
       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;
+  // TODO(eseckler): Also expose CreateStartupTraceWriter() ?
+
+  // 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;
+
+  // Whether the service accepted a shared memory buffer provided by the
+  // producer.
+  virtual bool IsShmemProvidedByProducer() const = 0;
 
   // Called in response to a Producer::Flush(request_id) call after all data
   // for the flush request has been committed.
@@ -129,6 +136,12 @@
   // This informs the service to activate any of these triggers if any tracing
   // session was waiting for them.
   virtual void ActivateTriggers(const std::vector<std::string>&) = 0;
+
+  // Emits a synchronization barrier to linearize with the service. When
+  // |callback| is invoked, the caller has the guarantee that the service has
+  // seen and processed all the requests sent by this producer prior to the
+  // Sync() call. Used mainly in tests.
+  virtual void Sync(std::function<void()> callback) = 0;
 };  // class ProducerEndpoint.
 
 // The API for the Consumer port of the Service.
@@ -190,25 +203,25 @@
   // Will call OnTraceStats().
   virtual void GetTraceStats() = 0;
 
-  enum ObservableEventType : uint32_t {
-    kNone = 0,
-    kDataSourceInstances = 1 << 0
-  };
-
-  // Start or stop observing events of selected types. |enabled_event_types|
-  // specifies the types of events to observe in a bitmask (see
-  // ObservableEventType enum). To disable observing, pass
-  // ObservableEventType::kNone. Will call OnObservableEvents() repeatedly
-  // whenever an event of an enabled ObservableEventType occurs.
-  //
+  // Start or stop observing events of selected types. |events_mask| specifies
+  // the types of events to observe in a bitmask of ObservableEvents::Type.
+  // To disable observing, pass 0.
+  // Will call OnObservableEvents() repeatedly whenever an event of an enabled
+  // ObservableEventType occurs.
   // TODO(eseckler): Extend this to support producers & data sources.
-  virtual void ObserveEvents(uint32_t enabled_event_types) = 0;
+  virtual void ObserveEvents(uint32_t events_mask) = 0;
 
   // Used to obtain the list of connected data sources and other info about
   // the tracing service.
   using QueryServiceStateCallback =
       std::function<void(bool success, const TracingServiceState&)>;
   virtual void QueryServiceState(QueryServiceStateCallback) = 0;
+
+  // Used for feature detection. Makes sense only when the consumer and the
+  // service talk over IPC and can be from different versions.
+  using QueryCapabilitiesCallback =
+      std::function<void(const TracingServiceCapabilities&)>;
+  virtual void QueryCapabilities(QueryCapabilitiesCallback) = 0;
 };  // class ConsumerEndpoint.
 
 // The public API of the tracing Service business logic.
@@ -249,24 +262,42 @@
 
   // Connects a Producer instance and obtains a ProducerEndpoint, which is
   // essentially a 1:1 channel between one Producer and the Service.
+  //
   // The caller has to guarantee that the passed Producer will be alive as long
-  // as the returned ProducerEndpoint is alive.
-  // Both the passed Prodcer and the returned ProducerEndpint must live on the
-  // same task runner of the service, specifically:
+  // as the returned ProducerEndpoint is alive. Both the passed Producer and the
+  // returned ProducerEndpoint must live on the same task runner of the service,
+  // specifically:
   // 1) The Service will call Producer::* methods on the Service's task runner.
   // 2) The Producer should call ProducerEndpoint::* methods only on the
   //    service's task runner, except for ProducerEndpoint::CreateTraceWriter(),
-  //    which can be called on any thread.
-  // To disconnect just destroy the returned ProducerEndpoint object. It is safe
-  // to destroy the Producer once the Producer::OnDisconnect() has been invoked.
+  //    which can be called on any thread. To disconnect just destroy the
+  //    returned ProducerEndpoint object. It is safe to destroy the Producer
+  //    once the Producer::OnDisconnect() has been invoked.
+  //
   // |uid| is the trusted user id of the producer process, used by the consumers
-  // for validating the origin of trace data.
-  // |shared_memory_size_hint_bytes| and |shared_memory_page_size_hint_bytes|
-  // are optional hints on the size of the shared memory buffer and its pages.
-  // The service can ignore the hints (e.g., if the hints are unreasonably
-  // large or other sizes were configured in a tracing session's config).
-  // |in_process| enables the ProducerEndpoint to manage its own shared memory
-  // and enables use of |ProducerEndpoint::CreateTraceWriter|.
+  // for validating the origin of trace data. |shared_memory_size_hint_bytes|
+  // and |shared_memory_page_size_hint_bytes| are optional hints on the size of
+  // the shared memory buffer and its pages. The service can ignore the hints
+  // (e.g., if the hints are unreasonably large or other sizes were configured
+  // in a tracing session's config). |in_process| enables the ProducerEndpoint
+  // to manage its own shared memory and enables use of
+  // |ProducerEndpoint::CreateTraceWriter|.
+  //
+  // The producer can optionally provide a non-null |shm|, which the service
+  // will adopt for the connection to the producer, provided it is correctly
+  // sized. In this case, |shared_memory_page_size_hint_bytes| indicates the
+  // page size used in this SMB. The producer can use this mechanism to record
+  // tracing data to an SMB even before the tracing session is started by the
+  // service. This is used in Chrome to implement startup tracing. If the buffer
+  // is incorrectly sized, the service will discard the SMB and allocate a new
+  // one, provided to the producer via ProducerEndpoint::shared_memory() after
+  // OnTracingSetup(). To verify that the service accepted the SMB, the producer
+  // may check via ProducerEndpoint::IsShmemProvidedByProducer(). If the service
+  // accepted the SMB, the producer can then commit any data that is already in
+  // the SMB after the tracing session was started by the service via
+  // Producer::StartDataSource(). The |shm| will also be rejected when
+  // connecting to a service that is too old (pre Android-11).
+  //
   // Can return null in the unlikely event that service has too many producers
   // connected.
   virtual std::unique_ptr<ProducerEndpoint> ConnectProducer(
@@ -277,7 +308,8 @@
       bool in_process = false,
       ProducerSMBScrapingMode smb_scraping_mode =
           ProducerSMBScrapingMode::kDefault,
-      size_t shared_memory_page_size_hint_bytes = 0) = 0;
+      size_t shared_memory_page_size_hint_bytes = 0,
+      std::unique_ptr<SharedMemory> shm = nullptr) = 0;
 
   // Connects a Consumer instance and obtains a ConsumerEndpoint, which is
   // essentially a 1:1 channel between one Consumer and the Service.
diff --git a/include/perfetto/ext/tracing/ipc/producer_ipc_client.h b/include/perfetto/ext/tracing/ipc/producer_ipc_client.h
index 6c1bdba..5469ded 100644
--- a/include/perfetto/ext/tracing/ipc/producer_ipc_client.h
+++ b/include/perfetto/ext/tracing/ipc/producer_ipc_client.h
@@ -21,6 +21,8 @@
 #include <string>
 
 #include "perfetto/base/export.h"
+#include "perfetto/ext/tracing/core/shared_memory.h"
+#include "perfetto/ext/tracing/core/shared_memory_arbiter.h"
 #include "perfetto/ext/tracing/core/tracing_service.h"
 
 namespace perfetto {
@@ -36,12 +38,19 @@
  public:
   // Connects to the producer port of the Service listening on the given
   // |service_sock_name|. If the connection is successful, the OnConnect()
-  // method will be invoked asynchronously on the passed Producer interface.
-  // If the connection fails, OnDisconnect() will be invoked instead.
-  // The returned ProducerEndpoint serves also to delimit the scope of the
-  // callbacks invoked on the Producer interface: no more Producer callbacks are
-  // invoked immediately after its destruction and any pending callback will be
-  // dropped.
+  // method will be invoked asynchronously on the passed Producer interface. If
+  // the connection fails, OnDisconnect() will be invoked instead. The returned
+  // ProducerEndpoint serves also to delimit the scope of the callbacks invoked
+  // on the Producer interface: no more Producer callbacks are invoked
+  // immediately after its destruction and any pending callback will be dropped.
+  // To provide a producer-allocated shared memory buffer, both |shm| and
+  // |shm_arbiter| should be set. |shm_arbiter| should be an unbound
+  // SharedMemoryArbiter instance. When |shm| and |shm_arbiter| are provided,
+  // the service will attempt to adopt the provided SMB. If this fails, the
+  // ProducerEndpoint will disconnect, but the SMB and arbiter will remain valid
+  // until the client is destroyed.
+  //
+  // TODO(eseckler): Support adoption failure more gracefully.
   static std::unique_ptr<TracingService::ProducerEndpoint> Connect(
       const char* service_sock_name,
       Producer*,
@@ -50,7 +59,9 @@
       TracingService::ProducerSMBScrapingMode smb_scraping_mode =
           TracingService::ProducerSMBScrapingMode::kDefault,
       size_t shared_memory_size_hint_bytes = 0,
-      size_t shared_memory_page_size_hint_bytes = 0);
+      size_t shared_memory_page_size_hint_bytes = 0,
+      std::unique_ptr<SharedMemory> shm = nullptr,
+      std::unique_ptr<SharedMemoryArbiter> shm_arbiter = nullptr);
 
  protected:
   ProducerIPCClient() = delete;
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/profiling/parse_smaps.h b/include/perfetto/profiling/parse_smaps.h
new file mode 100644
index 0000000..47d11c9
--- /dev/null
+++ b/include/perfetto/profiling/parse_smaps.h
@@ -0,0 +1,126 @@
+/*
+ * 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 is a header-only file because we want to use this in the ART plugin,
+// which should not depend on any Perfetto compilation units other than the
+// client API.
+
+#ifndef INCLUDE_PERFETTO_PROFILING_PARSE_SMAPS_H_
+#define INCLUDE_PERFETTO_PROFILING_PARSE_SMAPS_H_
+
+#include <string>
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <inttypes.h>
+
+namespace perfetto {
+namespace profiling {
+
+struct SmapsEntry {
+  int64_t size_kb = -1;
+  int64_t private_dirty_kb = -1;
+  int64_t swap_kb = -1;
+  std::string pathname;
+};
+
+struct SmapsParserState {
+  bool parsed_header = false;
+  SmapsEntry current_entry{};
+};
+
+template <typename T>
+static bool ParseSmaps(FILE* f, T callback) {
+  SmapsParserState state;
+
+  size_t line_size = 1024;
+  char* line = static_cast<char*>(malloc(line_size));
+
+  for (;;) {
+    errno = 0;
+    ssize_t rd = getline(&line, &line_size, f);
+    if (rd == -1) {
+      free(line);
+      if (state.parsed_header)
+        callback(state.current_entry);
+      return errno == 0;
+    } else {
+      if (line[rd - 1] == '\n') {
+        line[rd - 1] = '\0';
+        rd--;
+      }
+      if (!ParseSmapsLine(line, static_cast<size_t>(rd), &state, callback)) {
+        free(line);
+        return false;
+      }
+    }
+  }
+}
+
+static inline const char* FindNthToken(const char* line,
+                                       size_t n,
+                                       size_t size) {
+  size_t tokens = 0;
+  bool parsing_token = false;
+  for (size_t i = 0; i < size; ++i) {
+    if (!parsing_token && line[i] != ' ') {
+      parsing_token = true;
+      if (tokens++ == n)
+        return line + static_cast<ssize_t>(i);
+    }
+    if (line[i] == ' ')
+      parsing_token = false;
+  }
+  return nullptr;
+}
+
+template <typename T>
+static bool ParseSmapsLine(char* line,
+                           size_t size,
+                           SmapsParserState* state,
+                           T callback) {
+  char* first_token_end = static_cast<char*>(memchr(line, ' ', size));
+  if (first_token_end == nullptr || first_token_end == line)
+    return false;  // Malformed.
+  bool is_header = *(first_token_end - 1) != ':';
+
+  if (is_header) {
+    if (state->parsed_header)
+      callback(state->current_entry);
+
+    state->current_entry = {};
+    const char* last_token_begin = FindNthToken(line, 5u, size);
+    if (last_token_begin)
+      state->current_entry.pathname.assign(last_token_begin);
+    state->parsed_header = true;
+    return true;
+  }
+  if (!state->parsed_header)
+    return false;
+
+  sscanf(line, "Size: %" PRId64 " kB", &state->current_entry.size_kb);
+  sscanf(line, "Swap: %" PRId64 " kB", &state->current_entry.swap_kb);
+  sscanf(line, "Private_Dirty: %" PRId64 " kB",
+         &state->current_entry.private_dirty_kb);
+  return true;
+}
+
+}  // namespace profiling
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_PROFILING_PARSE_SMAPS_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/protozero/contiguous_memory_range.h b/include/perfetto/protozero/contiguous_memory_range.h
index 7089d0b..d00715b 100644
--- a/include/perfetto/protozero/contiguous_memory_range.h
+++ b/include/perfetto/protozero/contiguous_memory_range.h
@@ -30,7 +30,7 @@
 
   inline bool is_valid() const { return begin != nullptr; }
   inline void reset() { begin = nullptr; }
-  inline size_t size() { return static_cast<size_t>(end - begin); }
+  inline size_t size() const { return static_cast<size_t>(end - begin); }
 };
 
 }  // namespace protozero
diff --git a/include/perfetto/protozero/field.h b/include/perfetto/protozero/field.h
index 69b2a15..98e5e1d 100644
--- a/include/perfetto/protozero/field.h
+++ b/include/perfetto/protozero/field.h
@@ -20,6 +20,7 @@
 #include <stdint.h>
 
 #include <string>
+#include <vector>
 
 #include "perfetto/base/logging.h"
 #include "perfetto/protozero/contiguous_memory_range.h"
@@ -184,9 +185,16 @@
 
   // Serializes the field back into a proto-encoded byte stream and appends it
   // to |dst|. |dst| is resized accordingly.
-  void SerializeAndAppendTo(std::string* dst);
+  void SerializeAndAppendTo(std::string* dst) const;
+
+  // Serializes the field back into a proto-encoded byte stream and appends it
+  // to |dst|. |dst| is resized accordingly.
+  void SerializeAndAppendTo(std::vector<uint8_t>* dst) const;
 
  private:
+  template <typename Container>
+  void SerializeAndAppendToInternal(Container* dst) const;
+
   // Fields are deliberately not initialized to keep the class trivially
   // constructible. It makes a large perf difference for ProtoDecoder.
 
diff --git a/include/perfetto/protozero/proto_decoder.h b/include/perfetto/protozero/proto_decoder.h
index b58e64e..d23af33 100644
--- a/include/perfetto/protozero/proto_decoder.h
+++ b/include/perfetto/protozero/proto_decoder.h
@@ -40,7 +40,7 @@
 // (see proto_decoder_fuzzer.cc).
 // This class serves also as a building block for TypedProtoDecoder, used when
 // the schema is known at compile time.
-class ProtoDecoder {
+class PERFETTO_EXPORT ProtoDecoder {
  public:
   // Creates a ProtoDecoder using the given |buffer| with size |length| bytes.
   ProtoDecoder(const void* buffer, size_t length)
@@ -108,6 +108,10 @@
     FindNextMatchingId();
   }
 
+  // Constructs an invalid iterator.
+  RepeatedFieldIterator()
+      : field_id_(0u), iter_(nullptr), end_(nullptr), last_(nullptr) {}
+
   explicit operator bool() const { return iter_ != end_; }
   const Field& field() const { return *iter_; }
 
@@ -273,7 +277,7 @@
 // [ field 0 (invalid) ] [ fields 1 .. N ] [ repeated fields ]
 //                                        ^                  ^
 //                                        num_fields_        size_
-class TypedProtoDecoderBase : public ProtoDecoder {
+class PERFETTO_EXPORT TypedProtoDecoderBase : public ProtoDecoder {
  public:
   // If the field |id| is known at compile time, prefer the templated
   // specialization at<kFieldNumber>().
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 b584126..cc41758 100644
--- a/include/perfetto/trace_processor/basic_types.h
+++ b/include/perfetto/trace_processor/basic_types.h
@@ -85,10 +85,22 @@
     return value;
   }
 
-  double AsDouble() {
-    assert(type == kDouble);
+  double AsDouble() const {
+    PERFETTO_CHECK(type == kDouble);
     return double_value;
   }
+  int64_t AsLong() const {
+    PERFETTO_CHECK(type == kLong);
+    return long_value;
+  }
+  const char* AsString() const {
+    PERFETTO_CHECK(type == kString);
+    return string_value;
+  }
+  const void* AsBytes() const {
+    PERFETTO_CHECK(type == kBytes);
+    return bytes_value;
+  }
 
   bool is_null() const { return type == Type::kNull; }
 
diff --git a/include/perfetto/trace_processor/read_trace.h b/include/perfetto/trace_processor/read_trace.h
index ac2053d..3431e7d 100644
--- a/include/perfetto/trace_processor/read_trace.h
+++ b/include/perfetto/trace_processor/read_trace.h
@@ -18,6 +18,7 @@
 #define INCLUDE_PERFETTO_TRACE_PROCESSOR_READ_TRACE_H_
 
 #include <functional>
+#include <vector>
 
 #include "perfetto/base/export.h"
 #include "perfetto/trace_processor/status.h"
@@ -32,6 +33,10 @@
     const char* filename,
     const std::function<void(uint64_t parsed_size)>& progress_callback = {});
 
+util::Status PERFETTO_EXPORT DecompressTrace(const uint8_t* data,
+                                             size_t size,
+                                             std::vector<uint8_t>* output);
+
 }  // namespace trace_processor
 }  // namespace perfetto
 
diff --git a/include/perfetto/tracing/BUILD.gn b/include/perfetto/tracing/BUILD.gn
index 5c7a79f..2fd0810 100644
--- a/include/perfetto/tracing/BUILD.gn
+++ b/include/perfetto/tracing/BUILD.gn
@@ -14,8 +14,8 @@
 
 source_set("tracing") {
   public_deps = [
-    "../../../gn:default_deps",
     "../../../protos/perfetto/common:cpp",
+    "../../../protos/perfetto/config/track_event:cpp",
     "../../../protos/perfetto/trace:zero",
     "../../../protos/perfetto/trace/interned_data:zero",
     "../../../protos/perfetto/trace/track_event:zero",
@@ -31,6 +31,8 @@
     "event_context.h",
     "internal/basic_types.h",
     "internal/data_source_internal.h",
+    "internal/in_process_tracing_backend.h",
+    "internal/system_tracing_backend.h",
     "internal/tracing_muxer.h",
     "internal/tracing_tls.h",
     "internal/track_event_data_source.h",
diff --git a/include/perfetto/tracing/core/BUILD.gn b/include/perfetto/tracing/core/BUILD.gn
index a7f4a4c..c961c2d 100644
--- a/include/perfetto/tracing/core/BUILD.gn
+++ b/include/perfetto/tracing/core/BUILD.gn
@@ -23,6 +23,7 @@
     "data_source_config.h",
     "data_source_descriptor.h",
     "trace_config.h",
+    "tracing_service_capabilities.h",
     "tracing_service_state.h",
   ]
 }
@@ -31,7 +32,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/core/forward_decls.h b/include/perfetto/tracing/core/forward_decls.h
index 890e211..8d9ceb2 100644
--- a/include/perfetto/tracing/core/forward_decls.h
+++ b/include/perfetto/tracing/core/forward_decls.h
@@ -57,6 +57,7 @@
 class ObservableEvents;
 class TraceConfig;
 class TraceStats;
+class TracingServiceCapabilities;
 class TracingServiceState;
 
 }  // namespace gen
@@ -69,6 +70,8 @@
 using ObservableEvents = ::perfetto::protos::gen::ObservableEvents;
 using TraceConfig = ::perfetto::protos::gen::TraceConfig;
 using TraceStats = ::perfetto::protos::gen::TraceStats;
+using TracingServiceCapabilities =
+    ::perfetto::protos::gen::TracingServiceCapabilities;
 using TracingServiceState = ::perfetto::protos::gen::TracingServiceState;
 
 }  // namespace perfetto
diff --git a/include/perfetto/tracing/core/tracing_service_capabilities.h b/include/perfetto/tracing/core/tracing_service_capabilities.h
new file mode 100644
index 0000000..ecb7440
--- /dev/null
+++ b/include/perfetto/tracing/core/tracing_service_capabilities.h
@@ -0,0 +1,28 @@
+/*
+ * 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_CORE_TRACING_SERVICE_CAPABILITIES_H_
+#define INCLUDE_PERFETTO_TRACING_CORE_TRACING_SERVICE_CAPABILITIES_H_
+
+// Creates the aliases in the ::perfetto namespace, doing things like:
+// using ::perfetto::Foo = ::perfetto::protos::gen::Foo.
+// See comments in forward_decls.h for the historical reasons of this
+// indirection layer.
+#include "perfetto/tracing/core/forward_decls.h"
+
+#include "protos/perfetto/common/tracing_service_capabilities.gen.h"
+
+#endif  // INCLUDE_PERFETTO_TRACING_CORE_TRACING_SERVICE_CAPABILITIES_H_
diff --git a/include/perfetto/tracing/data_source.h b/include/perfetto/tracing/data_source.h
index 1a2f57e..7780406 100644
--- a/include/perfetto/tracing/data_source.h
+++ b/include/perfetto/tracing/data_source.h
@@ -45,6 +45,14 @@
 
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 
+// PERFETTO_COMPONENT_EXPORT is used to mark symbols in Perfetto's headers
+// (typically templates) that are defined by the user outside of Perfetto and
+// should be made visible outside the current module. (e.g., in Chrome's
+// component build).
+#if !defined(PERFETTO_COMPONENT_EXPORT)
+#define PERFETTO_COMPONENT_EXPORT
+#endif
+
 namespace perfetto {
 namespace internal {
 class TracingMuxerImpl;
@@ -188,6 +196,11 @@
       tls_inst_->trace_writer->Flush(cb);
     }
 
+    // Returns the number of bytes written on the current thread by the current
+    // data-source since its creation.
+    // This can be useful for splitting protos that might grow very large.
+    uint64_t written() { return tls_inst_->trace_writer->written(); }
+
     // Returns a RAII handle to access the data source instance, guaranteeing
     // that it won't be deleted on another thread (because of trace stopping)
     // while accessing it from within the Trace() lambda.
@@ -370,7 +383,8 @@
   // tracing is enabled and the data source is selected.
   // This must be called after Tracing::Initialize().
   // Can return false to signal failure if attemping to register more than
-  // kMaxDataSources (32) data sources types.
+  // kMaxDataSources (32) data sources types or if tracing hasn't been
+  // initialized.
   static bool Register(const DataSourceDescriptor& descriptor) {
     // Silences -Wunused-variable warning in case the trace method is not used
     // by the translation unit that declares the data source.
@@ -381,6 +395,8 @@
       return std::unique_ptr<DataSourceBase>(new DataSourceType());
     };
     auto* tracing_impl = internal::TracingMuxer::Get();
+    if (!tracing_impl)
+      return false;
     return tracing_impl->RegisterDataSource(descriptor, factory,
                                             &static_state_);
   }
@@ -459,14 +475,26 @@
 #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(...) \
-  PERFETTO_INTERNAL_SWALLOW_SEMICOLON()
+// This macro must be used once for each data source next to the data source's
+// declaration.
+#define PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS(...)              \
+  template <>                                                         \
+  PERFETTO_COMPONENT_EXPORT perfetto::internal::DataSourceStaticState \
+      perfetto::DataSource<__VA_ARGS__>::static_state_;               \
+  template <>                                                         \
+  PERFETTO_COMPONENT_EXPORT thread_local perfetto::internal::         \
+      DataSourceThreadLocalState*                                     \
+          perfetto::DataSource<__VA_ARGS__>::tls_state_
 
-// Not needed -- only here for backwards compatibility.
-// TODO(skyostil): Remove this macro.
-#define PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(...) \
-  PERFETTO_INTERNAL_SWALLOW_SEMICOLON()
+// This macro must be used once for each data source in one source file to
+// allocate static storage for the data source's static state.
+#define PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(...)               \
+  template <>                                                         \
+  PERFETTO_COMPONENT_EXPORT perfetto::internal::DataSourceStaticState \
+      perfetto::DataSource<__VA_ARGS__>::static_state_{};             \
+  template <>                                                         \
+  PERFETTO_COMPONENT_EXPORT thread_local perfetto::internal::         \
+      DataSourceThreadLocalState*                                     \
+          perfetto::DataSource<__VA_ARGS__>::tls_state_ = nullptr
 
 #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..2b479fe 100644
--- a/include/perfetto/tracing/debug_annotation.h
+++ b/include/perfetto/tracing/debug_annotation.h
@@ -18,11 +18,27 @@
 #define INCLUDE_PERFETTO_TRACING_DEBUG_ANNOTATION_H_
 
 #include "perfetto/base/export.h"
+#include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
 
 #include <stdint.h>
 
+#include <memory>
 #include <string>
 
+namespace {
+// std::underlying_type can't be used with non-enum types, so we need this
+// indirection.
+template <typename T, bool = std::is_enum<T>::value>
+struct safe_underlying_type {
+  using type = typename std::underlying_type<T>::type;
+};
+
+template <typename T>
+struct safe_underlying_type<T, false> {
+  using type = T;
+};
+}  // namespace
+
 namespace perfetto {
 namespace protos {
 namespace pbzero {
@@ -41,20 +57,81 @@
 };
 
 namespace internal {
+// Overloads for all the supported built in debug annotation types. Numeric
+// types are handled with templates to avoid problems with overloading
+// platform-specific types (e.g., size_t).
+void PERFETTO_EXPORT WriteDebugAnnotation(protos::pbzero::DebugAnnotation*,
+                                          const char*);
+void PERFETTO_EXPORT WriteDebugAnnotation(protos::pbzero::DebugAnnotation*,
+                                          const std::string&);
+void PERFETTO_EXPORT WriteDebugAnnotation(protos::pbzero::DebugAnnotation*,
+                                          const void*);
+void PERFETTO_EXPORT WriteDebugAnnotation(protos::pbzero::DebugAnnotation*,
+                                          const DebugAnnotation&);
 
-// Overloads for all the supported built in debug annotation types.
-void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*, bool);
-void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*, uint64_t);
-void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*, unsigned);
-void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*, int64_t);
-void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*, int);
-void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*, double);
-void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*, float);
-void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*, const char*);
-void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*, const std::string&);
-void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*, const void*);
-void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*,
-                          const DebugAnnotation&);
+template <typename T>
+void WriteDebugAnnotation(
+    protos::pbzero::DebugAnnotation* annotation,
+    T value,
+    typename std::enable_if<std::is_floating_point<T>::value>::type* =
+        nullptr) {
+  annotation->set_double_value(static_cast<double>(value));
+}
+
+template <typename T>
+void WriteDebugAnnotation(
+    protos::pbzero::DebugAnnotation* annotation,
+    T value,
+    typename std::enable_if<std::is_integral<T>::value &&
+                            !std::is_same<T, bool>::value &&
+                            std::is_signed<T>::value>::type* = nullptr) {
+  annotation->set_int_value(value);
+}
+
+template <typename T>
+void WriteDebugAnnotation(
+    protos::pbzero::DebugAnnotation* annotation,
+    T value,
+    typename std::enable_if<
+        std::is_enum<T>::value &&
+        std::is_signed<typename safe_underlying_type<T>::type>::value>::type* =
+        nullptr) {
+  annotation->set_int_value(value);
+}
+
+template <typename T>
+void WriteDebugAnnotation(
+    protos::pbzero::DebugAnnotation* annotation,
+    T value,
+    typename std::enable_if<std::is_enum<T>::value &&
+                            std::is_unsigned<typename safe_underlying_type<
+                                T>::type>::value>::type* = nullptr) {
+  annotation->set_uint_value(value);
+}
+
+template <typename T>
+void WriteDebugAnnotation(
+    protos::pbzero::DebugAnnotation* annotation,
+    T value,
+    typename std::enable_if<std::is_integral<T>::value &&
+                            !std::is_same<T, bool>::value &&
+                            std::is_unsigned<T>::value>::type* = nullptr) {
+  annotation->set_uint_value(value);
+}
+
+template <typename T>
+void WriteDebugAnnotation(
+    protos::pbzero::DebugAnnotation* annotation,
+    T value,
+    typename std::enable_if<std::is_same<T, bool>::value>::type* = nullptr) {
+  annotation->set_bool_value(value);
+}
+
+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/src/tracing/internal/in_process_tracing_backend.h b/include/perfetto/tracing/internal/in_process_tracing_backend.h
similarity index 79%
rename from src/tracing/internal/in_process_tracing_backend.h
rename to include/perfetto/tracing/internal/in_process_tracing_backend.h
index 8d07ae7..20c7bd2 100644
--- a/src/tracing/internal/in_process_tracing_backend.h
+++ b/include/perfetto/tracing/internal/in_process_tracing_backend.h
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACING_INTERNAL_IN_PROCESS_TRACING_BACKEND_H_
-#define SRC_TRACING_INTERNAL_IN_PROCESS_TRACING_BACKEND_H_
+#ifndef INCLUDE_PERFETTO_TRACING_INTERNAL_IN_PROCESS_TRACING_BACKEND_H_
+#define INCLUDE_PERFETTO_TRACING_INTERNAL_IN_PROCESS_TRACING_BACKEND_H_
 
+#include "perfetto/base/export.h"
 #include "perfetto/tracing/tracing_backend.h"
 
 namespace perfetto {
@@ -34,9 +35,9 @@
 // instance in-process. Instantiated when the embedder calls
 // Tracing::Initialize(kInProcessBackend). Solves most in-app-only tracing
 // use-cases.
-class InProcessTracingBackend : public TracingBackend {
+class PERFETTO_EXPORT InProcessTracingBackend : public TracingBackend {
  public:
-  static InProcessTracingBackend* GetInstance();
+  static TracingBackend* GetInstance();
 
   // TracingBackend implementation.
   std::unique_ptr<ProducerEndpoint> ConnectProducer(
@@ -54,4 +55,4 @@
 }  // namespace internal
 }  // namespace perfetto
 
-#endif  // SRC_TRACING_INTERNAL_IN_PROCESS_TRACING_BACKEND_H_
+#endif  // INCLUDE_PERFETTO_TRACING_INTERNAL_IN_PROCESS_TRACING_BACKEND_H_
diff --git a/src/tracing/internal/system_tracing_backend.h b/include/perfetto/tracing/internal/system_tracing_backend.h
similarity index 81%
rename from src/tracing/internal/system_tracing_backend.h
rename to include/perfetto/tracing/internal/system_tracing_backend.h
index ca70242..69a1cad 100644
--- a/src/tracing/internal/system_tracing_backend.h
+++ b/include/perfetto/tracing/internal/system_tracing_backend.h
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACING_INTERNAL_SYSTEM_TRACING_BACKEND_H_
-#define SRC_TRACING_INTERNAL_SYSTEM_TRACING_BACKEND_H_
+#ifndef INCLUDE_PERFETTO_TRACING_INTERNAL_SYSTEM_TRACING_BACKEND_H_
+#define INCLUDE_PERFETTO_TRACING_INTERNAL_SYSTEM_TRACING_BACKEND_H_
 
+#include "perfetto/base/export.h"
 #include "perfetto/tracing/tracing_backend.h"
 
 namespace perfetto {
@@ -34,9 +35,9 @@
 // together with system traces, useful to correlate on the timeline system
 // events (e.g. scheduling slices from the kernel) with in-app events.
 namespace internal {
-class SystemTracingBackend : public TracingBackend {
+class PERFETTO_EXPORT SystemTracingBackend : public TracingBackend {
  public:
-  static SystemTracingBackend* GetInstance();
+  static TracingBackend* GetInstance();
 
   // TracingBackend implementation.
   std::unique_ptr<ProducerEndpoint> ConnectProducer(
@@ -51,4 +52,4 @@
 }  // namespace internal
 }  // namespace perfetto
 
-#endif  // SRC_TRACING_INTERNAL_SYSTEM_TRACING_BACKEND_H_
+#endif  // INCLUDE_PERFETTO_TRACING_INTERNAL_SYSTEM_TRACING_BACKEND_H_
diff --git a/include/perfetto/tracing/internal/track_event_data_source.h b/include/perfetto/tracing/internal/track_event_data_source.h
index e6c8af0..1bdffec 100644
--- a/include/perfetto/tracing/internal/track_event_data_source.h
+++ b/include/perfetto/tracing/internal/track_event_data_source.h
@@ -19,15 +19,16 @@
 
 #include "perfetto/base/compiler.h"
 #include "perfetto/protozero/message_handle.h"
+#include "perfetto/tracing/core/data_source_config.h"
 #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/config/track_event/track_event_config.gen.h"
 #include "protos/perfetto/trace/track_event/track_event.pbzero.h"
 
 #include <type_traits>
-#include <unordered_map>
 
 namespace perfetto {
 namespace internal {
@@ -91,7 +92,10 @@
  public:
   // DataSource implementation.
   void OnSetup(const DataSourceBase::SetupArgs& args) override {
-    TrackEventInternal::EnableTracing(*Registry, *args.config,
+    auto config_raw = args.config->track_event_config_raw();
+    bool ok = config_.ParseFromArray(config_raw.data(), config_raw.size());
+    PERFETTO_DCHECK(ok);
+    TrackEventInternal::EnableTracing(*Registry, config_,
                                       args.internal_instance_index);
   }
 
@@ -105,6 +109,23 @@
     Base::template Trace([](typename Base::TraceContext ctx) { ctx.Flush(); });
   }
 
+  // Determine if tracing for the given static category is enabled.
+  template <size_t CategoryIndex>
+  static bool IsCategoryEnabled() {
+    return Registry->GetCategoryState(CategoryIndex)
+        ->load(std::memory_order_relaxed);
+  }
+
+  // Determine if tracing for the given dynamic category is enabled.
+  static bool IsDynamicCategoryEnabled(
+      const DynamicCategory& dynamic_category) {
+    bool enabled = false;
+    Base::template Trace([&](typename Base::TraceContext ctx) {
+      enabled = IsDynamicCategoryEnabled(&ctx, dynamic_category);
+    });
+    return enabled;
+  }
+
   // This is the inlined entrypoint for all track event trace points. It tries
   // to be as lightweight as possible in terms of instructions and aims to
   // compile down to an unlikely conditional jump to the actual trace writing
@@ -117,61 +138,125 @@
 
   // Once we've determined tracing to be enabled for this category, actually
   // write a trace event onto this thread's default track. Outlined to avoid
-  // bloating code at the actual trace point.
-  // TODO(skyostil): Investigate whether this should be fully outlined to reduce
-  // binary size.
+  // 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
+  // - Lambda + timestamp
+  // - One debug annotation
+  // - Two debug annotations
+  // - Track
+  // - Track + Lambda
+  // - Track + Lambda + timestamp
+  // - Track + one debug annotation
+  // - Track + two debug annotations
+
+  // Trace point which takes no arguments.
+  template <size_t CategoryIndex, typename CategoryType>
+  static void TraceForCategory(uint32_t instances,
+                               const CategoryType& dynamic_category,
+                               const char* event_name,
+                               perfetto::protos::pbzero::TrackEvent::Type type)
+      PERFETTO_NO_INLINE {
+    TraceForCategoryImpl<CategoryIndex>(instances, dynamic_category, 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) {},
-      typename std::enable_if<IsValidTraceLambda<ArgumentFunction>()>::type* =
-          nullptr) PERFETTO_NO_INLINE {
-    // We don't simply call TraceForCategory(..., Track(), ...) here, since that
-    // would add extra binary bloat to all trace points that target the default
-    // track.
-    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));
-          // There's no need to emit a track descriptor for the default track
-          // here since that's done in ResetIncrementalState().
-        });
+            typename CategoryType,
+            typename ArgumentFunction = void (*)(EventContext),
+            typename ArgumentFunctionCheck = typename std::enable_if<
+                IsValidTraceLambda<ArgumentFunction>()>::type>
+  static void TraceForCategory(uint32_t instances,
+                               const CategoryType& dynamic_category,
+                               const char* event_name,
+                               perfetto::protos::pbzero::TrackEvent::Type type,
+                               ArgumentFunction arg_function)
+      PERFETTO_NO_INLINE {
+    TraceForCategoryImpl<CategoryIndex>(
+        instances, dynamic_category, event_name, type, Track(),
+        TrackEventInternal::GetTimeNs(), std::move(arg_function));
+  }
+
+  // Trace point which takes a lambda function argument and an overridden
+  // timestamp. |timestamp| must be in nanoseconds in the trace clock timebase.
+  template <size_t CategoryIndex,
+            typename CategoryType,
+            typename ArgumentFunction = void (*)(EventContext),
+            typename ArgumentFunctionCheck = typename std::enable_if<
+                IsValidTraceLambda<ArgumentFunction>()>::type>
+  static void TraceForCategory(uint32_t instances,
+                               const CategoryType& dynamic_category,
+                               const char* event_name,
+                               perfetto::protos::pbzero::TrackEvent::Type type,
+                               uint64_t timestamp,
+                               ArgumentFunction arg_function)
+      PERFETTO_NO_INLINE {
+    TraceForCategoryImpl<CategoryIndex>(instances, dynamic_category, event_name,
+                                        type, Track(), timestamp,
+                                        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 CategoryType,
             typename TrackType,
-            typename ArgumentFunction = void (*)(EventContext)>
-  static void TraceForCategory(
-      uint32_t instances,
-      const char* event_name,
-      perfetto::protos::pbzero::TrackEvent::Type type,
-      const TrackType& track,
-      ArgumentFunction arg_function = [](EventContext) {},
-      typename std::enable_if<IsValidTraceLambda<ArgumentFunction>()>::type* =
-          nullptr,
-      typename std::enable_if<
-          std::is_convertible<TrackType, Track>::value>::type* = nullptr)
+            typename TrackTypeCheck = typename std::enable_if<
+                std::is_convertible<TrackType, Track>::value>::type>
+  static void TraceForCategory(uint32_t instances,
+                               const CategoryType& dynamic_category,
+                               const char* event_name,
+                               perfetto::protos::pbzero::TrackEvent::Type type,
+                               const TrackType& track) PERFETTO_NO_INLINE {
+    TraceForCategoryImpl<CategoryIndex>(instances, dynamic_category, event_name,
+                                        type, track);
+  }
+
+  // Trace point with a track and a lambda function.
+  template <size_t CategoryIndex,
+            typename TrackType,
+            typename CategoryType,
+            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 CategoryType& dynamic_category,
+                               const char* event_name,
+                               perfetto::protos::pbzero::TrackEvent::Type type,
+                               const TrackType& track,
+                               ArgumentFunction arg_function)
       PERFETTO_NO_INLINE {
-    PERFETTO_DCHECK(track);
-    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);
-          event_ctx.event()->set_track_uuid(track.uuid);
-          arg_function(std::move(event_ctx));
-          TrackEventInternal::WriteTrackDescriptorIfNeeded(
-              track, ctx.tls_inst_->trace_writer.get(),
-              ctx.GetIncrementalState());
-        });
+    TraceForCategoryImpl<CategoryIndex>(
+        instances, dynamic_category, event_name, type, track,
+        TrackEventInternal::GetTimeNs(), std::move(arg_function));
+  }
+
+  // Trace point with a track, a lambda function and an overridden timestamp.
+  // |timestamp| must be in nanoseconds in the trace clock timebase.
+  template <size_t CategoryIndex,
+            typename TrackType,
+            typename CategoryType,
+            typename ArgumentFunction = void (*)(EventContext),
+            typename ArgumentFunctionCheck = typename std::enable_if<
+                IsValidTraceLambda<ArgumentFunction>()>::type>
+  static void TraceForCategory(uint32_t instances,
+                               const CategoryType& dynamic_category,
+                               const char* event_name,
+                               perfetto::protos::pbzero::TrackEvent::Type type,
+                               const TrackType& track,
+                               uint64_t timestamp,
+                               ArgumentFunction arg_function)
+      PERFETTO_NO_INLINE {
+    TraceForCategoryImpl<CategoryIndex>(instances, dynamic_category, event_name,
+                                        type, track, timestamp,
+                                        std::move(arg_function));
   }
 
   // Trace point with one debug annotation.
@@ -185,57 +270,56 @@
   // to be inlined at the call site while the _inner_ function
   // (TraceForCategoryWithDebugAnnotations) is still outlined to minimize
   // overall binary size.
-  template <size_t CategoryIndex, typename ArgType>
+  template <size_t CategoryIndex, typename CategoryType, typename ArgType>
   static void TraceForCategory(uint32_t instances,
+                               const CategoryType& dynamic_category,
                                const char* event_name,
                                perfetto::protos::pbzero::TrackEvent::Type type,
                                const char* arg_name,
                                ArgType&& arg_value) PERFETTO_ALWAYS_INLINE {
-    TraceForCategoryWithDebugAnnotations<CategoryIndex, Track, ArgType>(
-        instances, event_name, type, Track(), arg_name,
+    TraceForCategoryWithDebugAnnotations<CategoryIndex, CategoryType, Track,
+                                         ArgType>(
+        instances, dynamic_category, event_name, type, Track(), arg_name,
         std::forward<ArgType>(arg_value));
   }
 
   // A one argument trace point which takes an explicit track.
-  template <size_t CategoryIndex, typename TrackType, typename ArgType>
+  template <size_t CategoryIndex,
+            typename CategoryType,
+            typename TrackType,
+            typename ArgType>
   static void TraceForCategory(uint32_t instances,
+                               const CategoryType& dynamic_category,
                                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,
+    TraceForCategoryWithDebugAnnotations<CategoryIndex, CategoryType, TrackType,
+                                         ArgType>(
+        instances, dynamic_category, event_name, type, track, arg_name,
         std::forward<ArgType>(arg_value));
   }
 
-  template <size_t CategoryIndex, typename TrackType, typename ArgType>
+  template <size_t CategoryIndex,
+            typename CategoryType,
+            typename TrackType,
+            typename ArgType>
   static void TraceForCategoryWithDebugAnnotations(
       uint32_t instances,
+      const CategoryType& dynamic_category,
       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);
-            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());
-          }
+    TraceForCategoryImpl<CategoryIndex>(
+        instances, dynamic_category, event_name, type, track,
+        TrackEventInternal::GetTimeNs(), [&](EventContext event_ctx) {
+          TrackEventInternal::AddDebugAnnotation(&event_ctx, arg_name,
+                                                 arg_value);
         });
   }
 
@@ -243,27 +327,33 @@
   // direct debug annotations. For more complicated arguments, you should
   // define your own argument type in track_event.proto and use a lambda to fill
   // it in your trace point.
-  template <size_t CategoryIndex, typename ArgType, typename ArgType2>
+  template <size_t CategoryIndex,
+            typename CategoryType,
+            typename ArgType,
+            typename ArgType2>
   static void TraceForCategory(uint32_t instances,
+                               const CategoryType& dynamic_category,
                                const char* event_name,
                                perfetto::protos::pbzero::TrackEvent::Type type,
                                const char* arg_name,
                                ArgType&& arg_value,
                                const char* arg_name2,
                                ArgType2&& arg_value2) PERFETTO_ALWAYS_INLINE {
-    TraceForCategoryWithDebugAnnotations<CategoryIndex, Track, ArgType,
-                                         ArgType2>(
-        instances, event_name, type, Track(), arg_name,
+    TraceForCategoryWithDebugAnnotations<CategoryIndex, CategoryType, Track,
+                                         ArgType, ArgType2>(
+        instances, dynamic_category, event_name, type, Track(), arg_name,
         std::forward<ArgType>(arg_value), arg_name2,
         std::forward<ArgType2>(arg_value2));
   }
 
   // A two argument trace point which takes an explicit track.
   template <size_t CategoryIndex,
+            typename CategoryType,
             typename TrackType,
             typename ArgType,
             typename ArgType2>
   static void TraceForCategory(uint32_t instances,
+                               const CategoryType& dynamic_category,
                                const char* event_name,
                                perfetto::protos::pbzero::TrackEvent::Type type,
                                const TrackType& track,
@@ -272,19 +362,21 @@
                                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,
+    TraceForCategoryWithDebugAnnotations<CategoryIndex, CategoryType, TrackType,
+                                         ArgType, ArgType2>(
+        instances, dynamic_category, event_name, type, track, arg_name,
         std::forward<ArgType>(arg_value), arg_name2,
         std::forward<ArgType2>(arg_value2));
   }
 
   template <size_t CategoryIndex,
+            typename CategoryType,
             typename TrackType,
             typename ArgType,
             typename ArgType2>
   static void TraceForCategoryWithDebugAnnotations(
       uint32_t instances,
+      const CategoryType& dynamic_category,
       const char* event_name,
       perfetto::protos::pbzero::TrackEvent::Type type,
       TrackType track,
@@ -293,25 +385,13 @@
       const char* arg_name2,
       typename internal::DebugAnnotationArg<ArgType2>::type arg_value2)
       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);
-            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());
-          }
+    TraceForCategoryImpl<CategoryIndex>(
+        instances, dynamic_category, event_name, type, track,
+        TrackEventInternal::GetTimeNs(), [&](EventContext event_ctx) {
+          TrackEventInternal::AddDebugAnnotation(&event_ctx, arg_name,
+                                                 arg_value);
+          TrackEventInternal::AddDebugAnnotation(&event_ctx, arg_name2,
+                                                 arg_value2);
         });
   }
 
@@ -321,6 +401,7 @@
     // Registration is performed out-of-line so users don't need to depend on
     // DataSourceDescriptor C++ bindings.
     return TrackEventInternal::Initialize(
+        *Registry,
         [](const DataSourceDescriptor& dsd) { return Base::Register(dsd); });
   }
 
@@ -347,16 +428,101 @@
     TrackRegistry::Get()->EraseTrack(track);
   }
 
+  // Returns the current trace timestamp in nanoseconds. Note the returned
+  // timebase may vary depending on the platform, but will always match the
+  // timestamps recorded by track events (see GetTraceClockId).
+  static uint64_t GetTraceTimeNs() { return TrackEventInternal::GetTimeNs(); }
+
+  // Returns the type of clock used by GetTraceTimeNs().
+  static constexpr protos::pbzero::ClockSnapshot::Clock::BuiltinClocks
+  GetTraceClockId() {
+    return TrackEventInternal::GetClockId();
+  }
+
  private:
   // Each category has its own enabled/disabled state, stored in the category
   // registry.
   template <size_t CategoryIndex>
   struct CategoryTracePointTraits {
     static constexpr std::atomic<uint8_t>* GetActiveInstances() {
+      static_assert(
+          CategoryIndex != TrackEventCategoryRegistry::kInvalidCategoryIndex,
+          "Invalid category index");
       return Registry->GetCategoryState(CategoryIndex);
     }
   };
 
+  // TODO(skyostil): Make |CategoryIndex| a regular parameter to reuse trace
+  // point code across different categories.
+  template <size_t CategoryIndex,
+            typename CategoryType,
+            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 CategoryType& dynamic_category,
+      const char* event_name,
+      perfetto::protos::pbzero::TrackEvent::Type type,
+      const TrackType& track = Track(),
+      uint64_t timestamp = TrackEventInternal::GetTimeNs(),
+      ArgumentFunction arg_function = [](EventContext) {
+      }) PERFETTO_ALWAYS_INLINE {
+    TraceWithInstances<CategoryIndex>(
+        instances, [&](typename Base::TraceContext ctx) {
+          // If this category is dynamic, first check whether it's enabled.
+          constexpr bool kIsDynamic =
+              CategoryIndex ==
+              TrackEventCategoryRegistry::kDynamicCategoryIndex;
+          if (kIsDynamic && !IsDynamicCategoryEnabled(
+                                &ctx, DynamicCategory{dynamic_category})) {
+            return;
+          }
+
+          {
+            // TODO(skyostil): Intern categories at compile time.
+            const Category* static_category =
+                kIsDynamic ? nullptr : Registry->GetCategory(CategoryIndex);
+            auto event_ctx = TrackEventInternal::WriteEvent(
+                ctx.tls_inst_->trace_writer.get(), ctx.GetIncrementalState(),
+                static_category, event_name, type, timestamp);
+            if (kIsDynamic) {
+              Category category{
+                  Category::FromDynamicCategory(dynamic_category)};
+              category.ForEachGroupMember(
+                  [&](const char* member_name, size_t name_size) {
+                    event_ctx.event()->add_categories(member_name, name_size);
+                    return true;
+                  });
+            }
+            if (track)
+              event_ctx.event()->set_track_uuid(track.uuid);
+            arg_function(std::move(event_ctx));
+          }  // event_ctx
+
+          if (track) {
+            TrackEventInternal::WriteTrackDescriptorIfNeeded(
+                track, ctx.tls_inst_->trace_writer.get(),
+                ctx.GetIncrementalState());
+          }
+        });
+  }
+
+  template <size_t CategoryIndex, typename Lambda>
+  static void TraceWithInstances(uint32_t instances,
+                                 Lambda lambda) PERFETTO_ALWAYS_INLINE {
+    if (CategoryIndex == TrackEventCategoryRegistry::kDynamicCategoryIndex) {
+      Base::template TraceWithInstances(instances, std::move(lambda));
+    } else {
+      Base::template TraceWithInstances<
+          CategoryTracePointTraits<CategoryIndex>>(instances,
+                                                   std::move(lambda));
+    }
+  }
+
   // Records a track descriptor into the track descriptor registry and, if we
   // are tracing, also mirrors the descriptor into the trace.
   template <typename TrackType>
@@ -370,6 +536,31 @@
           track, ctx.tls_inst_->trace_writer.get());
     });
   }
+
+  // Determines if the given dynamic category is enabled, first by checking the
+  // per-trace writer cache or by falling back to computing it based on the
+  // trace config for the given session.
+  static bool IsDynamicCategoryEnabled(
+      typename Base::TraceContext* ctx,
+      const DynamicCategory& dynamic_category) {
+    auto incr_state = ctx->GetIncrementalState();
+    auto it = incr_state->dynamic_categories.find(dynamic_category.name);
+    if (it == incr_state->dynamic_categories.end()) {
+      // We haven't seen this category before. Let's figure out if it's enabled.
+      // This requires grabbing a lock to read the session's trace config.
+      auto ds = ctx->GetDataSourceLocked();
+      Category category{Category::FromDynamicCategory(dynamic_category)};
+      bool enabled = TrackEventInternal::IsCategoryEnabled(
+          *Registry, ds->config_, category);
+      // TODO(skyostil): Cap the size of |dynamic_categories|.
+      incr_state->dynamic_categories[dynamic_category.name] = enabled;
+      return enabled;
+    }
+    return it->second;
+  }
+
+  // Config for the current tracing session.
+  protos::gen::TrackEventConfig config_;
 };
 
 }  // namespace internal
diff --git a/include/perfetto/tracing/internal/track_event_internal.h b/include/perfetto/tracing/internal/track_event_internal.h
index a4ee050..82de8d7 100644
--- a/include/perfetto/tracing/internal/track_event_internal.h
+++ b/include/perfetto/tracing/internal/track_event_internal.h
@@ -23,14 +23,19 @@
 #include "perfetto/tracing/debug_annotation.h"
 #include "perfetto/tracing/trace_writer_base.h"
 #include "perfetto/tracing/track.h"
+#include "protos/perfetto/trace/clock_snapshot.pbzero.h"
 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
 #include "protos/perfetto/trace/track_event/track_event.pbzero.h"
 
-#include <set>
+#include <unordered_map>
 
 namespace perfetto {
 class EventContext;
+struct Category;
 namespace protos {
+namespace gen {
+class TrackEventConfig;
+}  // namespace gen
 namespace pbzero {
 class DebugAnnotation;
 }  // namespace pbzero
@@ -77,28 +82,38 @@
   // 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;
+
+  // Dynamically registered category names that have been encountered during
+  // this tracing session. The value in the map indicates whether the category
+  // is enabled or disabled.
+  std::unordered_map<std::string, bool> dynamic_categories;
 };
 
 // The backend portion of the track event trace point implemention. Outlined to
 // a separate .cc file so it can be shared by different track event category
 // namespaces.
-class TrackEventInternal {
+class PERFETTO_EXPORT TrackEventInternal {
  public:
   static bool Initialize(
+      const TrackEventCategoryRegistry&,
       bool (*register_data_source)(const DataSourceDescriptor&));
 
   static void EnableTracing(const TrackEventCategoryRegistry& registry,
-                            const DataSourceConfig& config,
+                            const protos::gen::TrackEventConfig& config,
                             uint32_t instance_index);
   static void DisableTracing(const TrackEventCategoryRegistry& registry,
                              uint32_t instance_index);
+  static bool IsCategoryEnabled(const TrackEventCategoryRegistry& registry,
+                                const protos::gen::TrackEventConfig& config,
+                                const Category& category);
 
   static perfetto::EventContext WriteEvent(
       TraceWriterBase*,
       TrackEventIncrementalState*,
-      const char* category,
+      const Category* category,
       const char* name,
-      perfetto::protos::pbzero::TrackEvent::Type);
+      perfetto::protos::pbzero::TrackEvent::Type,
+      uint64_t timestamp = GetTimeNs());
 
   template <typename T>
   static void AddDebugAnnotation(perfetto::EventContext* event_ctx,
@@ -130,8 +145,21 @@
         track, NewTracePacket(trace_writer, GetTimeNs()));
   }
 
- private:
+  // Get the current time in nanoseconds in the trace clock timebase.
   static uint64_t GetTimeNs();
+
+  // Get the clock used by GetTimeNs().
+  static constexpr protos::pbzero::ClockSnapshot::Clock::BuiltinClocks
+  GetClockId() {
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX) && \
+    !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    return protos::pbzero::ClockSnapshot::Clock::BOOTTIME;
+#else
+    return protos::pbzero::ClockSnapshot::Clock::MONOTONIC;
+#endif
+  }
+
+ private:
   static void ResetIncrementalState(TraceWriterBase*, uint64_t timestamp);
   static protozero::MessageHandle<protos::pbzero::TracePacket> NewTracePacket(
       TraceWriterBase*,
diff --git a/include/perfetto/tracing/internal/track_event_macros.h b/include/perfetto/tracing/internal/track_event_macros.h
index bfe6692..b5b6303 100644
--- a/include/perfetto/tracing/internal/track_event_macros.h
+++ b/include/perfetto/tracing/internal/track_event_macros.h
@@ -40,8 +40,7 @@
 //
 #define PERFETTO_INTERNAL_DECLARE_CATEGORIES(...)                             \
   namespace internal {                                                        \
-  constexpr ::perfetto::internal::TrackEventCategory kCategories[] = {        \
-      __VA_ARGS__};                                                           \
+  constexpr ::perfetto::Category kCategories[] = {__VA_ARGS__};               \
   constexpr size_t kCategoryCount =                                           \
       sizeof(kCategories) / sizeof(kCategories[0]);                           \
   /* The per-instance enable/disable state per category */                    \
@@ -62,8 +61,8 @@
       kConstExprCategoryRegistry(kCategoryCount,                              \
                                  &kCategories[0],                             \
                                  &g_category_state_storage[0]);               \
-  extern const ::perfetto::internal::TrackEventCategoryRegistry               \
-      kCategoryRegistry;                                                      \
+  PERFETTO_COMPONENT_EXPORT extern const ::perfetto::internal::               \
+      TrackEventCategoryRegistry kCategoryRegistry;                           \
   static_assert(kConstExprCategoryRegistry.ValidateCategories(),              \
                 "Invalid category names found");                              \
   }  // namespace internal
@@ -72,57 +71,82 @@
 #define PERFETTO_INTERNAL_CATEGORY_STORAGE()                     \
   namespace internal {                                           \
   std::atomic<uint8_t> g_category_state_storage[kCategoryCount]; \
-  constexpr ::perfetto::internal::TrackEventCategoryRegistry     \
-      kCategoryRegistry(kCategoryCount,                          \
-                        &kCategories[0],                         \
-                        &g_category_state_storage[0]);           \
+  PERFETTO_COMPONENT_EXPORT constexpr ::perfetto::internal::     \
+      TrackEventCategoryRegistry kCategoryRegistry(              \
+          kCategoryCount,                                        \
+          &kCategories[0],                                       \
+          &g_category_state_storage[0]);                         \
   }  // namespace internal
 
 // Defines the TrackEvent data source for the current track event namespace.
-#define PERFETTO_INTERNAL_DECLARE_TRACK_EVENT_DATA_SOURCE()              \
-  struct TrackEvent : public ::perfetto::internal::TrackEventDataSource< \
-                          TrackEvent, &internal::kCategoryRegistry> {}
+#define PERFETTO_INTERNAL_DECLARE_TRACK_EVENT_DATA_SOURCE() \
+  struct PERFETTO_COMPONENT_EXPORT TrackEvent               \
+      : public ::perfetto::internal::TrackEventDataSource<  \
+            TrackEvent, &internal::kCategoryRegistry> {}
 
 // At compile time, turns a category name represented by a static string into an
 // index into the current category registry. A build error will be generated if
-// the category hasn't been registered. See PERFETTO_DEFINE_CATEGORIES.
-#define PERFETTO_GET_CATEGORY_INDEX(category)                                \
-  ::perfetto::internal::TrackEventCategoryRegistry::Validate<                \
-      ::PERFETTO_TRACK_EVENT_NAMESPACE::internal::kConstExprCategoryRegistry \
-          .Find(category)>()
-
-// Efficiently determines whether tracing is enabled for the given category, and
-// if so, emits one trace event with the given arguments.
-#define PERFETTO_INTERNAL_TRACK_EVENT(category, ...)                      \
-  ::PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent::CallIfCategoryEnabled<    \
-      PERFETTO_GET_CATEGORY_INDEX(category)>([&](uint32_t instances) {    \
-    ::PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent::TraceForCategory<       \
-        PERFETTO_GET_CATEGORY_INDEX(category)>(instances, ##__VA_ARGS__); \
-  })
+// the category hasn't been registered or added to the list of allowed dynamic
+// categories. See PERFETTO_DEFINE_CATEGORIES.
+#define PERFETTO_GET_CATEGORY_INDEX(category)                                  \
+  ::PERFETTO_TRACK_EVENT_NAMESPACE::internal::kConstExprCategoryRegistry.Find( \
+      category,                                                                \
+      ::PERFETTO_TRACK_EVENT_NAMESPACE::internal::IsDynamicCategory(category))
 
 // Generate a unique variable name with a given prefix.
 #define PERFETTO_INTERNAL_CONCAT2(a, b) a##b
 #define PERFETTO_INTERNAL_CONCAT(a, b) PERFETTO_INTERNAL_CONCAT2(a, b)
-#define PERFETTO_INTERNAL_UID(prefix) PERFETTO_INTERNAL_CONCAT(prefix, __LINE__)
+#define PERFETTO_UID(prefix) PERFETTO_INTERNAL_CONCAT(prefix, __LINE__)
+
+// Efficiently determines whether tracing is enabled for the given category, and
+// if so, emits one trace event with the given arguments.
+#define PERFETTO_INTERNAL_TRACK_EVENT(category, ...)                      \
+  do {                                                                    \
+    namespace tns = ::PERFETTO_TRACK_EVENT_NAMESPACE;                     \
+    /* Compute the category index outside the lambda to work around a */  \
+    /* GCC 7 bug */                                                       \
+    constexpr auto PERFETTO_UID(kCatIndex) =                              \
+        PERFETTO_GET_CATEGORY_INDEX(category);                            \
+    if (tns::internal::IsDynamicCategory(category)) {                     \
+      tns::TrackEvent::CallIfEnabled([&](uint32_t instances) {            \
+        tns::TrackEvent::TraceForCategory<PERFETTO_UID(kCatIndex)>(       \
+            instances, category, ##__VA_ARGS__);                          \
+      });                                                                 \
+    } else {                                                              \
+      tns::TrackEvent::CallIfCategoryEnabled<PERFETTO_UID(kCatIndex)>(    \
+          [&](uint32_t instances) {                                       \
+            /* TODO(skyostil): Get rid of the category name parameter. */ \
+            tns::TrackEvent::TraceForCategory<PERFETTO_UID(kCatIndex)>(   \
+                instances, nullptr, ##__VA_ARGS__);                       \
+          });                                                             \
+    }                                                                     \
+  } while (false)
 
 #define PERFETTO_INTERNAL_SCOPED_TRACK_EVENT(category, name, ...)             \
-  struct {                                                                    \
+  struct PERFETTO_UID(ScopedEvent) {                                          \
     struct EventFinalizer {                                                   \
-      /* The int parameter is an implementation detail. It allows the      */ \
+      /* The parameter is an implementation detail. It allows the          */ \
       /* anonymous struct to use aggregate initialization to invoke the    */ \
       /* lambda (which emits the BEGIN event and returns an integer)       */ \
       /* with the proper reference capture for any                         */ \
       /* TrackEventArgumentFunction in |__VA_ARGS__|. This is required so  */ \
       /* that the scoped event is exactly ONE line and can't escape the    */ \
       /* scope if used in a single line if statement.                      */ \
-      EventFinalizer(int) {}                                                  \
+      EventFinalizer(...) {}                                                  \
       ~EventFinalizer() { TRACE_EVENT_END(category); }                        \
     } finalizer;                                                              \
-  } PERFETTO_INTERNAL_UID(scoped_event) {                                     \
+  } PERFETTO_UID(scoped_event) {                                              \
     [&]() {                                                                   \
       TRACE_EVENT_BEGIN(category, name, ##__VA_ARGS__);                       \
       return 0;                                                               \
     }()                                                                       \
   }
 
+#define PERFETTO_INTERNAL_CATEGORY_ENABLED(category)                         \
+  (::PERFETTO_TRACK_EVENT_NAMESPACE::internal::IsDynamicCategory(category)   \
+       ? ::PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent::                      \
+             IsDynamicCategoryEnabled(::perfetto::DynamicCategory(category)) \
+       : ::PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent::IsCategoryEnabled<    \
+             PERFETTO_GET_CATEGORY_INDEX(category)>())
+
 #endif  // INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_MACROS_H_
diff --git a/include/perfetto/tracing/trace_writer_base.h b/include/perfetto/tracing/trace_writer_base.h
index 2d95668..824be1a 100644
--- a/include/perfetto/tracing/trace_writer_base.h
+++ b/include/perfetto/tracing/trace_writer_base.h
@@ -38,6 +38,7 @@
   NewTracePacket() = 0;
 
   virtual void Flush(std::function<void()> callback = {}) = 0;
+  virtual uint64_t written() const = 0;
 };
 
 }  // namespace perfetto
diff --git a/include/perfetto/tracing/tracing.h b/include/perfetto/tracing/tracing.h
index 61e805c..76a42a3 100644
--- a/include/perfetto/tracing/tracing.h
+++ b/include/perfetto/tracing/tracing.h
@@ -25,11 +25,19 @@
 #include <string>
 #include <vector>
 
+#include "perfetto/base/compiler.h"
 #include "perfetto/base/export.h"
 #include "perfetto/base/logging.h"
 #include "perfetto/tracing/core/forward_decls.h"
+#include "perfetto/tracing/internal/in_process_tracing_backend.h"
+#include "perfetto/tracing/internal/system_tracing_backend.h"
+
 namespace perfetto {
 
+namespace internal {
+class TracingMuxerImpl;
+}
+
 class TracingBackend;
 class Platform;
 class TracingSession;  // Declared below.
@@ -75,16 +83,25 @@
   // 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;
+  friend class internal::TracingMuxerImpl;
+
+  // Used only by the DCHECK in tracing.cc, to check that the config is the
+  // same in case of re-initialization.
+  bool operator==(const TracingInitArgs& other) const {
+    return std::tie(backends, custom_backend, platform, shmem_size_hint_kb,
+                    shmem_page_size_hint_kb, in_process_backend_factory_,
+                    system_backend_factory_, dcheck_is_on_) ==
+           std::tie(other.backends, other.custom_backend, other.platform,
+                    other.shmem_size_hint_kb, other.shmem_page_size_hint_kb,
+                    other.in_process_backend_factory_,
+                    other.system_backend_factory_, other.dcheck_is_on_);
+  }
+
+  using BackendFactoryFunction = TracingBackend* (*)();
+  BackendFactoryFunction in_process_backend_factory_ = nullptr;
+  BackendFactoryFunction system_backend_factory_ = nullptr;
   bool dcheck_is_on_ = PERFETTO_DCHECK_IS_ON();
 };
 
@@ -93,7 +110,30 @@
  public:
   // Initializes Perfetto with the given backends in the calling process and/or
   // with a user-provided backend. No-op if called more than once.
-  static void Initialize(const TracingInitArgs&);
+  static inline void Initialize(const TracingInitArgs& args)
+      PERFETTO_ALWAYS_INLINE {
+    TracingInitArgs args_copy(args);
+    // This code is inlined to allow dead-code elimination for unused backends.
+    // This saves ~200 KB when not using the in-process backend (b/148198993).
+    // The logic behind it is the following:
+    // Nothing other than the code below references the two GetInstance()
+    // methods. From a linker-graph viewpoint, those GetInstance() pull in many
+    // other pieces of the codebase (e.g. InProcessTracingBackend pulls the
+    // whole TracingServiceImpl, SystemTracingBackend pulls the whole //ipc
+    // layer). Due to the inline, the compiler can see through the code and
+    // realize that some branches are always not taken. When that happens, no
+    // reference to the backends' GetInstance() is emitted and that allows the
+    // linker GC to get rid of the entire set of dependencies.
+    if (args.backends & kInProcessBackend) {
+      args_copy.in_process_backend_factory_ =
+          &internal::InProcessTracingBackend::GetInstance;
+    }
+    if (args.backends & kSystemBackend) {
+      args_copy.system_backend_factory_ =
+          &internal::SystemTracingBackend::GetInstance;
+    }
+    InitializeInternal(args_copy);
+  }
 
   // Start a new tracing session using the given tracing backend. Use
   // |kUnspecifiedBackend| to select an available backend automatically.
@@ -103,6 +143,8 @@
       BackendType = kUnspecifiedBackend);
 
  private:
+  static void InitializeInternal(const TracingInitArgs&);
+
   Tracing() = delete;
 };
 
@@ -162,7 +204,7 @@
   // after stopping. Reading the trace data is a destructive operation w.r.t.
   // contents of the trace buffer and is not idempotent.
   // A single ReadTrace() call can yield >1 callback invocations, until
-  // |has_more| is true.
+  // |has_more| is false.
   using ReadTraceCallback = std::function<void(ReadTraceCallbackArgs)>;
   virtual void ReadTrace(ReadTraceCallback) = 0;
 
diff --git a/include/perfetto/tracing/track.h b/include/perfetto/tracing/track.h
index 7e56de4..8a4df74 100644
--- a/include/perfetto/tracing/track.h
+++ b/include/perfetto/tracing/track.h
@@ -84,6 +84,12 @@
   explicit operator bool() const { return uuid; }
   void Serialize(protos::pbzero::TrackDescriptor*) const;
 
+  // Construct a global track with identifier |id|.
+  //
+  // Beware: the globally unique |id| should be chosen carefully to avoid
+  // accidental clashes with track identifiers emitted by other producers.
+  static Track Global(uint64_t id) { return Track(id, Track()); }
+
  protected:
   static Track MakeThreadTrack(base::PlatformThreadId tid_) {
     return Track(static_cast<uint64_t>(tid_), MakeProcessTrack());
diff --git a/include/perfetto/tracing/track_event.h b/include/perfetto/tracing/track_event.h
index 3d8e02f..8eebf76 100644
--- a/include/perfetto/tracing/track_event.h
+++ b/include/perfetto/tracing/track_event.h
@@ -25,6 +25,8 @@
 #include "perfetto/tracing/track_event_category_registry.h"
 #include "protos/perfetto/trace/track_event/track_event.pbzero.h"
 
+#include <type_traits>
+
 // This file contains a set of macros designed for instrumenting applications
 // with track event trace points. While the underlying TrackEvent API can also
 // be used directly, doing so efficiently requires some care (e.g., to avoid
@@ -39,9 +41,9 @@
 //   e.g., my_tracing.h:
 //
 //       PERFETTO_DEFINE_CATEGORIES(
-//           PERFETTO_CATEGORY(base),
-//           PERFETTO_CATEGORY(v8),
-//           PERFETTO_CATEGORY(cc));
+//           perfetto::Category("base"),
+//           perfetto::Category("v8"),
+//           perfetto::Category("cc"));
 //
 //   Then in a single .cc file, e.g., my_tracing.cc:
 //
@@ -125,10 +127,72 @@
 #define PERFETTO_TRACK_EVENT_NAMESPACE perfetto
 #endif
 
-// A name for a single category. Wrapped in a macro in case we need to introduce
-// more fields in the future.
+// Deprecated; see perfetto::Category().
 #define PERFETTO_CATEGORY(name) \
-  { #name }
+  ::perfetto::Category { #name }
+
+// Internal helpers for determining if a given category is defined at build or
+// runtime.
+namespace PERFETTO_TRACK_EVENT_NAMESPACE {
+namespace internal {
+
+// By default no statically defined categories are dynamic, but this can be
+// overridden with PERFETTO_DEFINE_TEST_CATEGORY_PREFIXES.
+template <typename... T>
+constexpr bool IsDynamicCategory(const char*) {
+  return false;
+}
+
+// Explicitly dynamic categories are always dynamic.
+constexpr bool IsDynamicCategory(const ::perfetto::DynamicCategory&) {
+  return true;
+}
+
+}  // namespace internal
+}  // namespace PERFETTO_TRACK_EVENT_NAMESPACE
+
+namespace perfetto {
+
+// A wrapper for marking strings that can't be determined to be static at build
+// time, but are in fact static.
+class PERFETTO_EXPORT StaticString final {
+ public:
+  const char* value;
+
+  operator const char*() const { return value; }
+};
+
+namespace internal {
+
+template <typename T = void>
+constexpr bool IsStaticString(const char*) {
+  return true;
+}
+
+template <typename T = void>
+constexpr bool IsStaticString(...) {
+  return false;
+}
+
+}  // namespace internal
+}  // namespace perfetto
+
+// Normally all categories are defined statically at build-time (see
+// PERFETTO_DEFINE_CATEGORIES). However, some categories are only used for
+// testing, and we shouldn't publish them to the tracing service or include them
+// in a production binary. Use this macro to define a list of prefixes for these
+// types of categories. Note that trace points using these categories will be
+// slightly less efficient compared to regular trace points.
+#define PERFETTO_DEFINE_TEST_CATEGORY_PREFIXES(...)                       \
+  namespace PERFETTO_TRACK_EVENT_NAMESPACE {                              \
+  namespace internal {                                                    \
+  template <>                                                             \
+  constexpr bool IsDynamicCategory(const char* name) {                    \
+    return ::perfetto::internal::IsStringInPrefixList(name, __VA_ARGS__); \
+  }                                                                       \
+  } /* namespace internal */                                              \
+  } /* namespace PERFETTO_TRACK_EVENT_NAMESPACE */                        \
+  PERFETTO_INTERNAL_SWALLOW_SEMICOLON()
 
 // Register the set of available categories by passing a list of categories to
 // this macro: PERFETTO_CATEGORY(cat1), PERFETTO_CATEGORY(cat2), ...
@@ -139,7 +203,9 @@
   /* The track event data source for this set of categories */ \
   PERFETTO_INTERNAL_DECLARE_TRACK_EVENT_DATA_SOURCE();         \
   } /* namespace PERFETTO_TRACK_EVENT_NAMESPACE */             \
-  PERFETTO_INTERNAL_SWALLOW_SEMICOLON()
+  PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS(                 \
+      PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent,              \
+      perfetto::internal::TrackEventDataSourceTraits)
 
 // Allocate storage for each category by using this macro once per track event
 // namespace.
@@ -147,29 +213,60 @@
   namespace PERFETTO_TRACK_EVENT_NAMESPACE {       \
   PERFETTO_INTERNAL_CATEGORY_STORAGE()             \
   } /* namespace PERFETTO_TRACK_EVENT_NAMESPACE */ \
-  PERFETTO_INTERNAL_SWALLOW_SEMICOLON()
+  PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(      \
+      PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent,  \
+      perfetto::internal::TrackEventDataSourceTraits)
 
 // Ignore GCC warning about a missing argument for a variadic macro parameter.
 #pragma GCC system_header
 
+// Ensure that |string| is a static constant string.
+//
+// If you get a compiler failure here, you are most likely trying to use
+// TRACE_EVENT with a dynamic event name. There are two ways to fix this:
+//
+// 1) If the event name is actually dynamic (e.g., std::string), write it into
+//    the event manually:
+//
+//      TRACE_EVENT("category", nullptr, [&](perfetto::EventContext ctx) {
+//        ctx.event()->set_name(dynamic_name);
+//      });
+//
+// 2) If the name is static, but the pointer is computed at runtime, wrap it
+//    with perfetto::StaticString:
+//
+//      TRACE_EVENT("category", perfetto::StaticString{name});
+//
+//    DANGER: Using perfetto::StaticString with strings whose contents change
+//            dynamically can cause silent trace data corruption.
+//
+#define PERFETTO_GET_STATIC_STRING(string)                                 \
+  [&]() {                                                                  \
+    static_assert(                                                         \
+        std::is_same<decltype(string), ::perfetto::StaticString>::value || \
+            ::perfetto::internal::IsStaticString(string),                  \
+        "String must be static");                                          \
+    return static_cast<const char*>(string);                               \
+  }()
+
 // 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.
 //
+// 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).
+//
 // |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);
 //  });
 //
-#define TRACE_EVENT_BEGIN(category, name, ...) \
-  PERFETTO_INTERNAL_TRACK_EVENT(               \
-      category, name,                          \
+#define TRACE_EVENT_BEGIN(category, name, ...)    \
+  PERFETTO_INTERNAL_TRACK_EVENT(                  \
+      category, PERFETTO_GET_STATIC_STRING(name), \
       ::perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_BEGIN, ##__VA_ARGS__)
 
 // End a slice under |category|.
@@ -183,14 +280,16 @@
   PERFETTO_INTERNAL_SCOPED_TRACK_EVENT(category, name, ##__VA_ARGS__)
 
 // 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(                                            \
-      category, name, ::perfetto::protos::pbzero::TrackEvent::TYPE_INSTANT, \
-      ##__VA_ARGS__)
+#define TRACE_EVENT_INSTANT(category, name, ...)  \
+  PERFETTO_INTERNAL_TRACK_EVENT(                  \
+      category, PERFETTO_GET_STATIC_STRING(name), \
+      ::perfetto::protos::pbzero::TrackEvent::TYPE_INSTANT, ##__VA_ARGS__)
 
-// TODO(skyostil): Add arguments.
-// TODO(skyostil): Add async events.
+// Efficiently determine if the given static or dynamic trace category or
+// category group is enabled for tracing.
+#define TRACE_EVENT_CATEGORY_ENABLED(category) \
+  PERFETTO_INTERNAL_CATEGORY_ENABLED(category)
+
 // TODO(skyostil): Add flow events.
 // TODO(skyostil): Add counters.
 
diff --git a/include/perfetto/tracing/track_event_category_registry.h b/include/perfetto/tracing/track_event_category_registry.h
index f31696b..e51c4d6 100644
--- a/include/perfetto/tracing/track_event_category_registry.h
+++ b/include/perfetto/tracing/track_event_category_registry.h
@@ -19,23 +19,181 @@
 
 #include "perfetto/tracing/data_source.h"
 
+#include <stddef.h>
+
 #include <atomic>
+#include <utility>
 
 namespace perfetto {
-namespace internal {
+class DynamicCategory;
 
 // A compile-time representation of a track event category. See
 // PERFETTO_DEFINE_CATEGORIES for registering your own categories.
-struct TrackEventCategory {
-  const char* const name;
+struct PERFETTO_EXPORT Category {
+  using Tags = std::array<const char*, 4>;
+
+  const char* const name = nullptr;
+  const char* const description = nullptr;
+  const Tags tags = {};
+
+  constexpr Category(const Category&) = default;
+  constexpr explicit Category(const char* name_)
+      : name(CheckIsValidCategory(name_)),
+        name_sizes_(ComputeNameSizes(name_)) {}
+
+  constexpr Category SetDescription(const char* description_) const {
+    return Category(name, description_, tags, name_sizes_);
+  }
+
+  template <typename... Args>
+  constexpr Category SetTags(Args&&... args) const {
+    return Category(name, description, {std::forward<Args>(args)...},
+                    name_sizes_);
+  }
+
+  // A comma separated list of multiple categories to be used in a single trace
+  // point.
+  static constexpr Category Group(const char* names) {
+    return Category(names, AllowGroup{});
+  }
+
+  // Used for parsing dynamic category groups. Note that |name| and
+  // |DynamicCategory| must outlive the returned object because the category
+  // name isn't copied.
+  static Category FromDynamicCategory(const char* name);
+  static Category FromDynamicCategory(const DynamicCategory&);
+
+  constexpr bool IsGroup() const { return GetNameSize(1) > 0; }
+
+  // Returns the number of character in the category name. Not valid for
+  // category groups.
+  size_t name_size() const {
+    PERFETTO_DCHECK(!IsGroup());
+    return GetNameSize(0);
+  }
+
+  // Iterates over all the members of this category group, or just the name of
+  // the category itself if this isn't a category group. Return false from
+  // |callback| to stop iteration.
+  template <typename T>
+  void ForEachGroupMember(T callback) const {
+    const char* name_ptr = name;
+    size_t i = 0;
+    while (size_t name_size = GetNameSize(i++)) {
+      if (!callback(name_ptr, name_size))
+        break;
+      name_ptr += name_size + 1;
+    }
+  }
+
+ private:
+  static constexpr size_t kMaxGroupSize = 4;
+  using NameSizes = std::array<uint8_t, kMaxGroupSize>;
+
+  constexpr Category(const char* name_,
+                     const char* description_,
+                     Tags tags_,
+                     NameSizes name_sizes)
+      : name(name_),
+        description(description_),
+        tags(tags_),
+        name_sizes_(name_sizes) {}
+
+  enum AllowGroup {};
+  constexpr Category(const char* name_, AllowGroup)
+      : name(CheckIsValidCategoryGroup(name_)),
+        name_sizes_(ComputeNameSizes(name_)) {}
+
+  constexpr size_t GetNameSize(size_t i) const {
+    return i < name_sizes_.size() ? name_sizes_[i] : 0;
+  }
+
+  static constexpr NameSizes ComputeNameSizes(const char* s) {
+    static_assert(kMaxGroupSize == 4, "Unexpected maximum category group size");
+    return NameSizes{{static_cast<uint8_t>(GetNthNameSize(0, s, s)),
+                      static_cast<uint8_t>(GetNthNameSize(1, s, s)),
+                      static_cast<uint8_t>(GetNthNameSize(2, s, s)),
+                      static_cast<uint8_t>(GetNthNameSize(3, s, s))}};
+  }
+
+  static constexpr ptrdiff_t GetNthNameSize(int n,
+                                            const char* start,
+                                            const char* end,
+                                            int counter = 0) {
+    return (!*end || *end == ',')
+               ? ((!*end || counter == n)
+                      ? (counter == n ? end - start : 0)
+                      : GetNthNameSize(n, end + 1, end + 1, counter + 1))
+               : GetNthNameSize(n, start, end + 1, counter);
+  }
+
+  static constexpr const char* CheckIsValidCategory(const char* n) {
+    // We just replace invalid input with a nullptr here; it will trigger a
+    // static assert in TrackEventCategoryRegistry::ValidateCategories().
+    return GetNthNameSize(1, n, n) ? nullptr : n;
+  }
+
+  static constexpr const char* CheckIsValidCategoryGroup(const char* n) {
+    // Same as above: replace invalid input with nullptr.
+    return !GetNthNameSize(1, n, n) || GetNthNameSize(kMaxGroupSize, n, n)
+               ? nullptr
+               : n;
+  }
+
+  // An array of lengths of the different names associated with this category.
+  // If this category doesn't represent a group of multiple categories, only the
+  // first element is non-zero.
+  const NameSizes name_sizes_ = {};
 };
 
+// Dynamically constructed category names should marked as such through this
+// container type to make it less likely for trace points to accidentally start
+// using dynamic categories. Events with dynamic categories will always be
+// slightly more expensive than regular events, so use them sparingly.
+class PERFETTO_EXPORT DynamicCategory final {
+ public:
+  explicit DynamicCategory(const std::string& name_) : name(name_) {}
+  explicit DynamicCategory(const char* name_) : name(name_) {}
+  DynamicCategory() {}
+  ~DynamicCategory() = default;
+
+  const std::string name;
+};
+
+namespace internal {
+
+constexpr const char* NullCategory(const char*) {
+  return nullptr;
+}
+
+perfetto::DynamicCategory NullCategory(const perfetto::DynamicCategory&);
+
+constexpr bool StringMatchesPrefix(const char* str, const char* prefix) {
+  return !*str ? !*prefix
+               : !*prefix ? true
+                          : *str != *prefix
+                                ? false
+                                : StringMatchesPrefix(str + 1, prefix + 1);
+}
+
+constexpr bool IsStringInPrefixList(const char*) {
+  return false;
+}
+
+template <typename... Args>
+constexpr bool IsStringInPrefixList(const char* str,
+                                    const char* prefix,
+                                    Args... args) {
+  return StringMatchesPrefix(str, prefix) ||
+         IsStringInPrefixList(str, std::forward<Args>(args)...);
+}
+
 // Holds all the registered categories for one category namespace. See
 // PERFETTO_DEFINE_CATEGORIES for building the registry.
-class TrackEventCategoryRegistry {
+class PERFETTO_EXPORT TrackEventCategoryRegistry {
  public:
   constexpr TrackEventCategoryRegistry(size_t category_count,
-                                       const TrackEventCategory* categories,
+                                       const Category* categories,
                                        std::atomic<uint8_t>* state_storage)
       : categories_(categories),
         category_count_(category_count),
@@ -49,7 +207,7 @@
   size_t category_count() const { return category_count_; }
 
   // Returns a category based on its index.
-  const TrackEventCategory* GetCategory(size_t index) const;
+  const Category* GetCategory(size_t index) const;
 
   // Turn tracing on or off for the given category in a track event data source
   // instance.
@@ -71,22 +229,17 @@
   // implementation and typically don't need to be called by other code.)
 
   // At compile time, turn a category name into an index into the registry.
-  // Returns kInvalidCategoryIndex if the category was not found.
+  // Returns kInvalidCategoryIndex if the category was not found, or
+  // kDynamicCategoryIndex if |is_dynamic| is true or a DynamicCategory was
+  // passed in.
   static constexpr size_t kInvalidCategoryIndex = static_cast<size_t>(-1);
-  constexpr size_t Find(const char* name, size_t index = 0) const {
-    return (index == category_count_) ? kInvalidCategoryIndex
-                                      : StringEq(categories_[index].name, name)
-                                            ? index
-                                            : Find(name, index + 1);
+  static constexpr size_t kDynamicCategoryIndex = static_cast<size_t>(-2);
+  constexpr size_t Find(const char* name, bool is_dynamic) const {
+    return CheckIsValidCategoryIndex(FindImpl(name, is_dynamic));
   }
 
-  // A helper for validating that a category was registered at compile time.
-  template <size_t CategoryIndex>
-  static constexpr size_t Validate() {
-    static_assert(CategoryIndex != kInvalidCategoryIndex,
-                  "A track event used an unknown category. Please add it to "
-                  "PERFETTO_DEFINE_CATEGORIES().");
-    return CategoryIndex;
+  constexpr size_t Find(const DynamicCategory&, bool) const {
+    return kDynamicCategoryIndex;
   }
 
   constexpr bool ValidateCategories(size_t index = 0) const {
@@ -99,8 +252,37 @@
 
  private:
   // TODO(skyostil): Make the compile-time routines nicer with C++14.
+  constexpr size_t FindImpl(const char* name,
+                            bool is_dynamic,
+                            size_t index = 0) const {
+    return is_dynamic ? kDynamicCategoryIndex
+                      : (index == category_count_)
+                            ? kInvalidCategoryIndex
+                            : StringEq(categories_[index].name, name)
+                                  ? index
+                                  : FindImpl(name, false, index + 1);
+  }
+
+  // A compile time helper for checking that a category index is valid.
+  static constexpr size_t CheckIsValidCategoryIndex(size_t index) {
+    // Relies on PERFETTO_CHECK() (and the surrounding lambda) being a
+    // non-constexpr function, which will fail the build if the given |index| is
+    // invalid. The funny formatting here is so that clang shows the comment
+    // below as part of the error message.
+    // clang-format off
+    return index != kInvalidCategoryIndex ? index : \
+        /* Invalid category -- add it to PERFETTO_DEFINE_CATEGORIES(). */ [] {
+        PERFETTO_CHECK(
+            false &&
+            "A track event used an unknown category. Please add it to "
+            "PERFETTO_DEFINE_CATEGORIES().");
+        return kInvalidCategoryIndex;
+      }();
+    // clang-format on
+  }
+
   static constexpr bool IsValidCategoryName(const char* name) {
-    return (*name == '\"' || *name == '*')
+    return (!name || *name == '\"' || *name == '*' || *name == ' ')
                ? false
                : *name ? IsValidCategoryName(name + 1) : true;
   }
@@ -110,7 +292,7 @@
                     : (!*a || !*b) ? (*a == *b) : StringEq(a + 1, b + 1);
   }
 
-  const TrackEventCategory* const categories_;
+  const Category* const categories_;
   const size_t category_count_;
   std::atomic<uint8_t>* const state_storage_;
 };
diff --git a/include/perfetto/tracing/track_event_interned_data_index.h b/include/perfetto/tracing/track_event_interned_data_index.h
index be4c128..cce2665 100644
--- a/include/perfetto/tracing/track_event_interned_data_index.h
+++ b/include/perfetto/tracing/track_event_interned_data_index.h
@@ -170,8 +170,12 @@
     : public internal::BaseTrackEventInternedDataIndex {
  public:
   // Return an interning id for |value|. The returned id can be immediately
-  // written to the trace.
-  static size_t Get(EventContext* ctx, const ValueType& value) {
+  // written to the trace. The optional |add_args| are passed to the Add()
+  // function.
+  template <typename... Args>
+  static size_t Get(EventContext* ctx,
+                    const ValueType& value,
+                    Args&&... add_args) {
     // First check if the value exists in the dictionary.
     auto index_for_field = GetOrCreateIndexForField(ctx->incremental_state_);
     size_t iid;
@@ -186,7 +190,7 @@
     PERFETTO_DCHECK(iid);
     InternedDataType::Add(
         ctx->incremental_state_->serialized_interned_data.get(), iid,
-        std::move(value));
+        std::move(value), std::forward<Args>(add_args)...);
     return iid;
   }
 
diff --git a/include/perfetto/tracing/track_event_legacy.h b/include/perfetto/tracing/track_event_legacy.h
index 8513024..7c8c4d4 100644
--- a/include/perfetto/tracing/track_event_legacy.h
+++ b/include/perfetto/tracing/track_event_legacy.h
@@ -23,6 +23,7 @@
 // to 1 activate the compatibility layer.
 
 #include "perfetto/base/compiler.h"
+#include "perfetto/tracing/track_event.h"
 
 #include <stdint.h>
 
@@ -30,8 +31,6 @@
 #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
 
@@ -39,6 +38,36 @@
 // Constants.
 // ----------------------------------------------------------------------------
 
+namespace perfetto {
+namespace legacy {
+
+enum TraceEventFlag {
+  kTraceEventFlagNone = 0,
+  kTraceEventFlagCopy = 1u << 0,
+  kTraceEventFlagHasId = 1u << 1,
+  kTraceEventFlagScopeOffset = 1u << 2,
+  kTraceEventFlagScopeExtra = 1u << 3,
+  kTraceEventFlagExplicitTimestamp = 1u << 4,
+  kTraceEventFlagAsyncTTS = 1u << 5,
+  kTraceEventFlagBindToEnclosing = 1u << 6,
+  kTraceEventFlagFlowIn = 1u << 7,
+  kTraceEventFlagFlowOut = 1u << 8,
+  kTraceEventFlagHasContextId = 1u << 9,
+  kTraceEventFlagHasProcessId = 1u << 10,
+  kTraceEventFlagHasLocalId = 1u << 11,
+  kTraceEventFlagHasGlobalId = 1u << 12,
+  // TODO(eseckler): Remove once we have native support for typed proto events
+  // in TRACE_EVENT macros.
+  kTraceEventFlagTypedProtoArgs = 1u << 15,
+  kTraceEventFlagJavaStringLiterals = 1u << 16,
+};
+
+enum PerfettoLegacyCurrentThreadId { kCurrentThreadId };
+
+}  // namespace legacy
+}  // namespace perfetto
+
+#if PERFETTO_ENABLE_LEGACY_TRACE_EVENTS
 // The following constants are defined in the global namespace, since they were
 // originally implemented as macros.
 
@@ -70,26 +99,38 @@
 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_NONE =
+    perfetto::legacy::kTraceEventFlagNone;
+static constexpr uint32_t TRACE_EVENT_FLAG_COPY =
+    perfetto::legacy::kTraceEventFlagCopy;
+static constexpr uint32_t TRACE_EVENT_FLAG_HAS_ID =
+    perfetto::legacy::kTraceEventFlagHasId;
+static constexpr uint32_t TRACE_EVENT_FLAG_SCOPE_OFFSET =
+    perfetto::legacy::kTraceEventFlagScopeOffset;
+static constexpr uint32_t TRACE_EVENT_FLAG_SCOPE_EXTRA =
+    perfetto::legacy::kTraceEventFlagScopeExtra;
+static constexpr uint32_t TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP =
+    perfetto::legacy::kTraceEventFlagExplicitTimestamp;
+static constexpr uint32_t TRACE_EVENT_FLAG_ASYNC_TTS =
+    perfetto::legacy::kTraceEventFlagAsyncTTS;
+static constexpr uint32_t TRACE_EVENT_FLAG_BIND_TO_ENCLOSING =
+    perfetto::legacy::kTraceEventFlagBindToEnclosing;
+static constexpr uint32_t TRACE_EVENT_FLAG_FLOW_IN =
+    perfetto::legacy::kTraceEventFlagFlowIn;
+static constexpr uint32_t TRACE_EVENT_FLAG_FLOW_OUT =
+    perfetto::legacy::kTraceEventFlagFlowOut;
+static constexpr uint32_t TRACE_EVENT_FLAG_HAS_CONTEXT_ID =
+    perfetto::legacy::kTraceEventFlagHasContextId;
+static constexpr uint32_t TRACE_EVENT_FLAG_HAS_PROCESS_ID =
+    perfetto::legacy::kTraceEventFlagHasProcessId;
+static constexpr uint32_t TRACE_EVENT_FLAG_HAS_LOCAL_ID =
+    perfetto::legacy::kTraceEventFlagHasLocalId;
+static constexpr uint32_t TRACE_EVENT_FLAG_HAS_GLOBAL_ID =
+    perfetto::legacy::kTraceEventFlagHasGlobalId;
+static constexpr uint32_t TRACE_EVENT_FLAG_TYPED_PROTO_ARGS =
+    perfetto::legacy::kTraceEventFlagTypedProtoArgs;
+static constexpr uint32_t TRACE_EVENT_FLAG_JAVA_STRING_LITERALS =
+    perfetto::legacy::kTraceEventFlagJavaStringLiterals;
 
 static constexpr uint32_t TRACE_EVENT_FLAG_SCOPE_MASK =
     TRACE_EVENT_FLAG_SCOPE_OFFSET | TRACE_EVENT_FLAG_SCOPE_EXTRA;
@@ -106,45 +147,407 @@
 
 // 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 uint8_t TRACE_EVENT_SCOPE_GLOBAL = 0u << 2;
+static constexpr uint8_t TRACE_EVENT_SCOPE_PROCESS = 1u << 2;
+static constexpr uint8_t TRACE_EVENT_SCOPE_THREAD = 2u << 2;
 
 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';
 
+static constexpr auto TRACE_EVENT_API_CURRENT_THREAD_ID =
+    perfetto::legacy::kCurrentThreadId;
+
+#endif  // PERFETTO_ENABLE_LEGACY_TRACE_EVENTS
+
 // ----------------------------------------------------------------------------
 // Internal legacy trace point implementation.
 // ----------------------------------------------------------------------------
 
-// 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)
+namespace perfetto {
+namespace legacy {
+
+// The following user-provided adaptors are used to serialize user-defined
+// thread id and time types into track events. For full compatibility, the user
+// should also define the following macros appropriately:
+//
+//   #define TRACE_TIME_TICKS_NOW() ...
+//   #define TRACE_TIME_NOW() ...
+
+// User-provided function to convert an abstract thread id into either a track
+// uuid or a pid/tid override. Return true if the conversion succeeded.
+template <typename T>
+bool ConvertThreadId(const T&,
+                     uint64_t* track_uuid_out,
+                     int32_t* pid_override_out,
+                     int32_t* tid_override_out);
+
+// User-provided function to convert an abstract timestamp into the trace clock
+// timebase in nanoseconds.
+template <typename T>
+uint64_t ConvertTimestampToTraceTimeNs(const T&);
+
+// Built-in implementation for events referring to the current thread.
+template <>
+bool PERFETTO_EXPORT ConvertThreadId(const PerfettoLegacyCurrentThreadId&,
+                                     uint64_t*,
+                                     int32_t*,
+                                     int32_t*);
+
+}  // namespace legacy
+
+namespace internal {
+
+// LegacyTraceId encapsulates an ID that can either be an integer or pointer.
+class PERFETTO_EXPORT LegacyTraceId {
+ public:
+  // Can be combined with WithScope.
+  class LocalId {
+   public:
+    explicit LocalId(const void* raw_id)
+        : raw_id_(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(raw_id))) {}
+    explicit LocalId(uint64_t raw_id) : raw_id_(raw_id) {}
+    uint64_t raw_id() const { return raw_id_; }
+
+   private:
+    uint64_t raw_id_;
+  };
+
+  // Can be combined with WithScope.
+  class GlobalId {
+   public:
+    explicit GlobalId(uint64_t raw_id) : raw_id_(raw_id) {}
+    uint64_t raw_id() const { return raw_id_; }
+
+   private:
+    uint64_t raw_id_;
+  };
+
+  class WithScope {
+   public:
+    WithScope(const char* scope, uint64_t raw_id)
+        : scope_(scope), raw_id_(raw_id) {}
+    WithScope(const char* scope, LocalId local_id)
+        : scope_(scope), raw_id_(local_id.raw_id()) {
+      id_flags_ = legacy::kTraceEventFlagHasLocalId;
+    }
+    WithScope(const char* scope, GlobalId global_id)
+        : scope_(scope), raw_id_(global_id.raw_id()) {
+      id_flags_ = legacy::kTraceEventFlagHasGlobalId;
+    }
+    WithScope(const char* scope, uint64_t prefix, uint64_t raw_id)
+        : scope_(scope), has_prefix_(true), prefix_(prefix), raw_id_(raw_id) {}
+    WithScope(const char* scope, uint64_t prefix, GlobalId global_id)
+        : scope_(scope),
+          has_prefix_(true),
+          prefix_(prefix),
+          raw_id_(global_id.raw_id()) {
+      id_flags_ = legacy::kTraceEventFlagHasGlobalId;
+    }
+    uint64_t raw_id() const { return raw_id_; }
+    const char* scope() const { return scope_; }
+    bool has_prefix() const { return has_prefix_; }
+    uint64_t prefix() const { return prefix_; }
+    uint32_t id_flags() const { return id_flags_; }
+
+   private:
+    const char* scope_ = nullptr;
+    bool has_prefix_ = false;
+    uint64_t prefix_;
+    uint64_t raw_id_;
+    uint32_t id_flags_ = legacy::kTraceEventFlagHasId;
+  };
+
+  LegacyTraceId(const void* raw_id)
+      : raw_id_(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(raw_id))) {
+    id_flags_ = legacy::kTraceEventFlagHasLocalId;
+  }
+  explicit LegacyTraceId(uint64_t raw_id) : raw_id_(raw_id) {}
+  explicit LegacyTraceId(uint32_t raw_id) : raw_id_(raw_id) {}
+  explicit LegacyTraceId(uint16_t raw_id) : raw_id_(raw_id) {}
+  explicit LegacyTraceId(uint8_t raw_id) : raw_id_(raw_id) {}
+  explicit LegacyTraceId(int64_t raw_id)
+      : raw_id_(static_cast<uint64_t>(raw_id)) {}
+  explicit LegacyTraceId(int32_t raw_id)
+      : raw_id_(static_cast<uint64_t>(raw_id)) {}
+  explicit LegacyTraceId(int16_t raw_id)
+      : raw_id_(static_cast<uint64_t>(raw_id)) {}
+  explicit LegacyTraceId(int8_t raw_id)
+      : raw_id_(static_cast<uint64_t>(raw_id)) {}
+  explicit LegacyTraceId(LocalId raw_id) : raw_id_(raw_id.raw_id()) {
+    id_flags_ = legacy::kTraceEventFlagHasLocalId;
+  }
+  explicit LegacyTraceId(GlobalId raw_id) : raw_id_(raw_id.raw_id()) {
+    id_flags_ = legacy::kTraceEventFlagHasGlobalId;
+  }
+  explicit LegacyTraceId(WithScope scoped_id)
+      : scope_(scoped_id.scope()),
+        has_prefix_(scoped_id.has_prefix()),
+        prefix_(scoped_id.prefix()),
+        raw_id_(scoped_id.raw_id()),
+        id_flags_(scoped_id.id_flags()) {}
+
+  uint64_t raw_id() const { return raw_id_; }
+  const char* scope() const { return scope_; }
+  bool has_prefix() const { return has_prefix_; }
+  uint64_t prefix() const { return prefix_; }
+  uint32_t id_flags() const { return id_flags_; }
+
+  void Write(protos::pbzero::TrackEvent::LegacyEvent*,
+             uint32_t event_flags) const;
+
+ private:
+  const char* scope_ = nullptr;
+  bool has_prefix_ = false;
+  uint64_t prefix_;
+  uint64_t raw_id_;
+  uint32_t id_flags_ = legacy::kTraceEventFlagHasId;
+};
+
+}  // namespace internal
+}  // namespace perfetto
+
+#if PERFETTO_ENABLE_LEGACY_TRACE_EVENTS
+
+namespace perfetto {
+namespace internal {
+
+class PERFETTO_EXPORT 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 (NeedLegacyFlags(phase, flags)) {
+      auto legacy_event = ctx.event()->set_legacy_event();
+      SetLegacyFlags(legacy_event, phase, flags);
+    }
+  }
+
+  template <typename ThreadIdType, typename... Args>
+  static void WriteLegacyEventWithIdAndTid(EventContext ctx,
+                                           char phase,
+                                           uint32_t flags,
+                                           const LegacyTraceId& id,
+                                           const ThreadIdType& thread_id,
+                                           Args&&... args) PERFETTO_NO_INLINE {
+    //
+    // Overrides to consider:
+    //
+    // 1. If we have an id, we need to write {unscoped,local,global}_id and/or
+    //    bind_id.
+    // 2. If we have a thread id, we need to write track_uuid() or
+    //    {pid,tid}_override. This happens in embedder code since the thread id
+    //    is embedder-specified.
+    // 3. If we have a timestamp, we need to write a different timestamp in the
+    //    trace packet itself and make sure TrackEvent won't write one
+    //    internally. This is already done at the call site.
+    //
+    flags |= id.id_flags();
+    AddDebugAnnotations(&ctx, std::forward<Args>(args)...);
+    int32_t pid_override = 0;
+    int32_t tid_override = 0;
+    uint64_t track_uuid = 0;
+    if (legacy::ConvertThreadId(thread_id, &track_uuid, &pid_override,
+                                &tid_override) &&
+        track_uuid) {
+      if (track_uuid != ThreadTrack::Current().uuid)
+        ctx.event()->set_track_uuid(track_uuid);
+    } else if (pid_override || tid_override) {
+      // Explicitly clear the track so the overrides below take effect.
+      ctx.event()->set_track_uuid(0);
+    } else {
+      // No pid/tid/track overrides => obey the flags instead.
+      SetTrackIfNeeded(&ctx, flags);
+    }
+    if (NeedLegacyFlags(phase, flags) || pid_override || tid_override) {
+      auto legacy_event = ctx.event()->set_legacy_event();
+      SetLegacyFlags(legacy_event, phase, flags);
+      if (id.id_flags())
+        id.Write(legacy_event, flags);
+      if (pid_override)
+        legacy_event->set_pid_override(pid_override);
+      if (tid_override)
+        legacy_event->set_tid_override(tid_override);
+    }
+  }
+
+  // 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) {
+    // Note: This avoids the need to set LegacyEvent::instant_event_scope.
+    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;
+    }
+  }
+
+  static bool NeedLegacyFlags(char phase, uint32_t flags) {
+    if (PhaseToType(phase) == protos::pbzero::TrackEvent::TYPE_UNSPECIFIED)
+      return true;
+    // TODO(skyostil): Implement/deprecate:
+    // - TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP
+    // - TRACE_EVENT_FLAG_HAS_CONTEXT_ID
+    // - TRACE_EVENT_FLAG_HAS_PROCESS_ID
+    // - TRACE_EVENT_FLAG_TYPED_PROTO_ARGS
+    // - TRACE_EVENT_FLAG_JAVA_STRING_LITERALS
+    return flags &
+           (TRACE_EVENT_FLAG_HAS_ID | TRACE_EVENT_FLAG_HAS_LOCAL_ID |
+            TRACE_EVENT_FLAG_HAS_GLOBAL_ID | TRACE_EVENT_FLAG_ASYNC_TTS |
+            TRACE_EVENT_FLAG_BIND_TO_ENCLOSING | TRACE_EVENT_FLAG_FLOW_IN |
+            TRACE_EVENT_FLAG_FLOW_OUT);
+  }
+
+  static void SetLegacyFlags(
+      protos::pbzero::TrackEvent::LegacyEvent* legacy_event,
+      char phase,
+      uint32_t flags) {
+    if (PhaseToType(phase) == protos::pbzero::TrackEvent::TYPE_UNSPECIFIED)
+      legacy_event->set_phase(phase);
+    if (flags & TRACE_EVENT_FLAG_ASYNC_TTS)
+      legacy_event->set_use_async_tts(true);
+    if (flags & TRACE_EVENT_FLAG_BIND_TO_ENCLOSING)
+      legacy_event->set_bind_to_enclosing(true);
+
+    const auto kFlowIn = TRACE_EVENT_FLAG_FLOW_IN;
+    const auto kFlowOut = TRACE_EVENT_FLAG_FLOW_OUT;
+    const auto kFlowInOut = kFlowIn | kFlowOut;
+    if ((flags & kFlowInOut) == kFlowInOut) {
+      legacy_event->set_flow_direction(
+          protos::pbzero::TrackEvent::LegacyEvent::FLOW_INOUT);
+    } else if (flags & kFlowIn) {
+      legacy_event->set_flow_direction(
+          protos::pbzero::TrackEvent::LegacyEvent::FLOW_IN);
+    } else if (flags & kFlowOut) {
+      legacy_event->set_flow_direction(
+          protos::pbzero::TrackEvent::LegacyEvent::FLOW_OUT);
+    }
+  }
+};
+
+}  // namespace internal
+}  // namespace perfetto
 
 // Implementations for the INTERNAL_* adapter macros used by the trace points
 // below.
-#define INTERNAL_TRACE_EVENT_ADD(...) PERFETTO_INTERNAL_EVENT_NOOP(__VA_ARGS__)
-#define INTERNAL_TRACE_EVENT_ADD_SCOPED(...) \
-  PERFETTO_INTERNAL_EVENT_NOOP(__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_EVENT_ADD(phase, category, name, flags, ...)      \
+  PERFETTO_INTERNAL_TRACK_EVENT(                                         \
+      category, ::perfetto::StaticString{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_TIME_TICKS_NOW() 0
-#define INTERNAL_TRACE_TIME_NOW() 0
+#define INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name, ...)        \
+  PERFETTO_INTERNAL_SCOPED_TRACK_EVENT(                             \
+      category, ::perfetto::StaticString{name},                     \
+      [&](perfetto::EventContext ctx) {                             \
+        using ::perfetto::internal::TrackEventLegacy;               \
+        TrackEventLegacy::AddDebugAnnotations(&ctx, ##__VA_ARGS__); \
+      })
+
+#define INTERNAL_TRACE_EVENT_ADD_SCOPED_WITH_FLOW(category, name, bind_id, \
+                                                  flags, ...)              \
+  PERFETTO_INTERNAL_SCOPED_TRACK_EVENT(                                    \
+      category, ::perfetto::StaticString{name},                            \
+      [&](perfetto::EventContext ctx) {                                    \
+        using ::perfetto::internal::TrackEventLegacy;                      \
+        ::perfetto::internal::LegacyTraceId trace_id{bind_id};             \
+        TrackEventLegacy::WriteLegacyEventWithIdAndTid(                    \
+            std::move(ctx), TRACE_EVENT_PHASE_BEGIN, flags, trace_id,      \
+            TRACE_EVENT_API_CURRENT_THREAD_ID, ##__VA_ARGS__);             \
+      })
+
+#define INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP(phase, category, name,   \
+                                                timestamp, flags, ...)   \
+  PERFETTO_INTERNAL_TRACK_EVENT(                                         \
+      category, ::perfetto::StaticString{name},                          \
+      ::perfetto::internal::TrackEventLegacy::PhaseToType(phase),        \
+      ::perfetto::legacy::ConvertTimestampToTraceTimeNs(timestamp),      \
+      [&](perfetto::EventContext ctx) {                                  \
+        using ::perfetto::internal::TrackEventLegacy;                    \
+        TrackEventLegacy::WriteLegacyEvent(std::move(ctx), phase, flags, \
+                                           ##__VA_ARGS__);               \
+      })
+
+#define INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                    \
+    phase, category, name, id, thread_id, timestamp, flags, ...)               \
+  PERFETTO_INTERNAL_TRACK_EVENT(                                               \
+      category, ::perfetto::StaticString{name},                                \
+      ::perfetto::internal::TrackEventLegacy::PhaseToType(phase),              \
+      ::perfetto::legacy::ConvertTimestampToTraceTimeNs(timestamp),            \
+      [&](perfetto::EventContext ctx) {                                        \
+        using ::perfetto::internal::TrackEventLegacy;                          \
+        ::perfetto::internal::LegacyTraceId trace_id{id};                      \
+        TrackEventLegacy::WriteLegacyEventWithIdAndTid(                        \
+            std::move(ctx), phase, flags, trace_id, thread_id, ##__VA_ARGS__); \
+      })
+
+#define INTERNAL_TRACE_EVENT_ADD_WITH_ID(phase, category, name, id, flags, \
+                                         ...)                              \
+  PERFETTO_INTERNAL_TRACK_EVENT(                                           \
+      category, ::perfetto::StaticString{name},                            \
+      ::perfetto::internal::TrackEventLegacy::PhaseToType(phase),          \
+      [&](perfetto::EventContext ctx) {                                    \
+        using ::perfetto::internal::TrackEventLegacy;                      \
+        ::perfetto::internal::LegacyTraceId trace_id{id};                  \
+        TrackEventLegacy::WriteLegacyEventWithIdAndTid(                    \
+            std::move(ctx), phase, flags, trace_id,                        \
+            TRACE_EVENT_API_CURRENT_THREAD_ID, ##__VA_ARGS__);             \
+      })
+
+#define INTERNAL_TRACE_EVENT_METADATA_ADD(category, name, ...)         \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_METADATA, category, name, \
+                           TRACE_EVENT_FLAG_NONE)
 
 // ----------------------------------------------------------------------------
 // Legacy tracing common API (adapted from trace_event_common.h).
@@ -806,10 +1209,9 @@
                                    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;                                               \
+#define TRACE_EVENT_CATEGORY_GROUP_ENABLED(category, ret) \
+  do {                                                    \
+    *ret = TRACE_EVENT_CATEGORY_ENABLED(category);        \
   } while (0)
 
 // Macro to efficiently determine, through polling, if a new trace has begun.
@@ -819,27 +1221,43 @@
     *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.
+// involvement from the embedder. APIs such as 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;                                         \
-  }()
+#define TRACE_ID_WITH_SCOPE(scope, ...) \
+  ::perfetto::internal::LegacyTraceId::WithScope(scope, ##__VA_ARGS__)
+
+// Use this for ids that are unique across processes. This allows different
+// processes to use the same id to refer to the same event.
+#define TRACE_ID_GLOBAL(id) ::perfetto::internal::LegacyTraceId::GlobalId(id)
+
+// Use this for ids that are unique within a single process. This allows
+// different processes to use the same id to refer to different events.
+#define TRACE_ID_LOCAL(id) ::perfetto::internal::LegacyTraceId::LocalId(id)
+
+// Returns a pointer to a uint8_t which indicates whether tracing is enabled for
+// the given category or not. A zero value means tracing is disabled and
+// non-zero indicates at least one tracing session for this category is active.
+// Note that callers should not make any assumptions at what each bit represents
+// in the status byte. Does not support dynamic categories.
+#define TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category)                 \
+  reinterpret_cast<const uint8_t*>(                                          \
+      [&] {                                                                  \
+        static_assert(                                                       \
+            !::PERFETTO_TRACK_EVENT_NAMESPACE::internal::IsDynamicCategory(  \
+                category),                                                   \
+            "Enabled flag pointers are not supported for dynamic trace "     \
+            "categories.");                                                  \
+      },                                                                     \
+      ::PERFETTO_TRACK_EVENT_NAMESPACE::internal::kConstExprCategoryRegistry \
+          .GetCategoryState(PERFETTO_GET_CATEGORY_INDEX(category)))
 
 #endif  // PERFETTO_ENABLE_LEGACY_TRACE_EVENTS
 
diff --git a/infra/ci/Makefile b/infra/ci/Makefile
index b122287..39764eb 100644
--- a/infra/ci/Makefile
+++ b/infra/ci/Makefile
@@ -110,8 +110,8 @@
 	gcloud beta compute --quiet --project=${PROJECT} \
 		instance-groups managed set-autoscaling ${GCE_GROUP_NAME} \
 		--zone ${ZONE} \
-		--min-num-replicas "2" \
-		--max-num-replicas "2" \
+		--min-num-replicas "${NUM_VMS}" \
+		--max-num-replicas "${NUM_VMS}" \
 		--cool-down-period "1800" \
 		--stackdriver-metric-filter "resource.type = \"global\"" \
 		--update-stackdriver-metric "custom.googleapis.com/perfetto-ci/ci_job_queue_len" \
diff --git a/infra/ci/config.py b/infra/ci/config.py
index 8426880..8b35141 100755
--- a/infra/ci/config.py
+++ b/infra/ci/config.py
@@ -47,10 +47,11 @@
 TRUSTED_EMAILS = '^.*@google.com$'
 
 GCE_VM_NAME = 'ci-worker'
-GCE_VM_TYPE = 'n1-standard-64'
+GCE_VM_TYPE = 'e2-standard-16'
 GCE_TEMPLATE = 'ci-worker-template'
 GCE_GROUP_NAME = 'ci'
-NUM_WORKERS_PER_VM = 10
+NUM_VMS = 6
+NUM_WORKERS_PER_VM = 5
 
 GCE_SCOPES = [
     'https://www.googleapis.com/auth/cloud-platform',
diff --git a/infra/oss-fuzz/build_fuzzers b/infra/oss-fuzz/build_fuzzers
index 19a36a2..57018b5 100755
--- a/infra/oss-fuzz/build_fuzzers
+++ b/infra/oss-fuzz/build_fuzzers
@@ -2,7 +2,7 @@
 
 set -euo pipefail
 
-$SRC/perfetto/tools/install-build-deps --no-android
+$SRC/perfetto/tools/install-build-deps
 
 mkdir -p $WORK/build
 
diff --git a/infra/perfetto-get.appspot.com/main.py b/infra/perfetto-get.appspot.com/main.py
index dc06974..71e2b12 100644
--- a/infra/perfetto-get.appspot.com/main.py
+++ b/infra/perfetto-get.appspot.com/main.py
@@ -40,7 +40,7 @@
     resource = resource.lower()
     if resource not in RESOURCES:
       self.error(404)
-      self.response.out.write('Rerource "%s" not found' % resource)
+      self.response.out.write('Resource "%s" not found' % resource)
       return
 
     url = BASE % RESOURCES[resource]
diff --git a/protos/perfetto/common/BUILD.gn b/protos/perfetto/common/BUILD.gn
index 6721740..c061639 100644
--- a/protos/perfetto/common/BUILD.gn
+++ b/protos/perfetto/common/BUILD.gn
@@ -27,6 +27,7 @@
     "observable_events.proto",
     "sys_stats_counters.proto",
     "trace_stats.proto",
+    "tracing_service_capabilities.proto",
     "tracing_service_state.proto",
     "track_event_descriptor.proto",
   ]
diff --git a/protos/perfetto/common/observable_events.proto b/protos/perfetto/common/observable_events.proto
index e3911e4..0bde227 100644
--- a/protos/perfetto/common/observable_events.proto
+++ b/protos/perfetto/common/observable_events.proto
@@ -21,9 +21,22 @@
 message ObservableEvents {
   enum Type {
     TYPE_UNSPECIFIED = 0;
+
     // State changes of data source instances associated with the consumer's
     // session. Note that not all data sources may support these notifications.
+    // See |will_notify_on_start/stop| in DataSourceDescriptor.
     TYPE_DATA_SOURCES_INSTANCES = 1;
+
+    // State change triggered when all data sources are in the STARTED state.
+    // For data sources that registered with |will_notify_on_start| this happens
+    // only after the data source has acked the start. This allows the consumer
+    // to synchronize with the data sources and to perform actions (e.g. start a
+    // test binary) only after trace recording is actually started.
+    // Introduced in Android 11 (R).
+    TYPE_ALL_DATA_SOURCES_STARTED = 2;
+
+    // Note: internally these are used as OR flags. Next values: 4, 8, 16, ...
+
     // TODO(eseckler): Extend this for producer & data source registrations.
   }
 
@@ -40,4 +53,5 @@
   }
 
   repeated DataSourceInstanceStateChange instance_state_changes = 1;
+  optional bool all_data_sources_started = 2;
 }
diff --git a/protos/perfetto/common/tracing_service_capabilities.proto b/protos/perfetto/common/tracing_service_capabilities.proto
new file mode 100644
index 0000000..123f9bf
--- /dev/null
+++ b/protos/perfetto/common/tracing_service_capabilities.proto
@@ -0,0 +1,38 @@
+/*
+ * 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";
+
+import "protos/perfetto/common/observable_events.proto";
+
+package perfetto.protos;
+
+message TracingServiceCapabilities {
+  // Whether the service supports QueryCapabilities() at all or not.
+  // This is only used at the C++ level to distinguish the case of talking to
+  // an older version of the service that doesn't support QueryCapabilities().
+  // In that case the IPC layer will just reject the unknown call, and the
+  // consumer_ipc_client_impl.cc will return an empty message where this field
+  // is false. In all other cases, this is always set to true.
+  optional bool has_query_capabilities = 1;
+
+  // The set of known events that can be passed to ConsumerPort.ObserveEvents().
+  repeated ObservableEvents.Type observable_events = 2;
+
+  // Whether the service supports TraceConfig.output_path (for asking traced to
+  // create the output file instead of passing a file descriptor).
+  optional bool has_trace_config_output_path = 3;
+}
diff --git a/protos/perfetto/common/track_event_descriptor.proto b/protos/perfetto/common/track_event_descriptor.proto
index 47d605a..00121f2 100644
--- a/protos/perfetto/common/track_event_descriptor.proto
+++ b/protos/perfetto/common/track_event_descriptor.proto
@@ -18,6 +18,12 @@
 
 package perfetto.protos;
 
+message TrackEventCategory {
+  optional string name = 1;
+  optional string description = 2;
+  repeated string tags = 3;
+}
+
 message TrackEventDescriptor {
-  repeated string available_categories = 1;
+  repeated TrackEventCategory available_categories = 1;
 }
diff --git a/protos/perfetto/config/BUILD.gn b/protos/perfetto/config/BUILD.gn
index 1ccbba4..8a4a3b8 100644
--- a/protos/perfetto/config/BUILD.gn
+++ b/protos/perfetto/config/BUILD.gn
@@ -31,6 +31,7 @@
     "process_stats:@TYPE@",
     "profiling:@TYPE@",
     "sys_stats:@TYPE@",
+    "track_event:@TYPE@",
   ]
 
   sources = [
@@ -45,7 +46,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/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/data_source_config.proto b/protos/perfetto/config/data_source_config.proto
index aa071d6..550c090 100644
--- a/protos/perfetto/config/data_source_config.proto
+++ b/protos/perfetto/config/data_source_config.proto
@@ -32,6 +32,7 @@
 import "protos/perfetto/config/profiling/perf_event_config.proto";
 import "protos/perfetto/config/sys_stats/sys_stats_config.proto";
 import "protos/perfetto/config/test_config.proto";
+import "protos/perfetto/config/track_event/track_event_config.proto";
 
 // The configuration that is passed to each data source when starting tracing.
 message DataSourceConfig {
@@ -100,6 +101,8 @@
   optional PerfEventConfig perf_event_config = 111 [lazy = true];
   // Data source name: vulkan.memory_tracker
   optional VulkanMemoryConfig vulkan_memory_config = 112 [lazy = true];
+  // Data source name: track_event
+  optional TrackEventConfig track_event_config = 113 [lazy = true];
 
   // Chrome is special as it doesn't use the perfetto IPC layer. We want to
   // avoid proto serialization and de-serialization there because that would
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/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/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index 100f150..88f748b 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -11,64 +11,6 @@
 
 package perfetto.protos;
 
-// Begin of protos/perfetto/common/android_log_constants.proto
-
-// Values from NDK's android/log.h.
-enum AndroidLogId {
-  LID_DEFAULT = 0;  // MAIN.
-  LID_RADIO = 1;
-  LID_EVENTS = 2;
-  LID_SYSTEM = 3;
-  LID_CRASH = 4;
-  LID_STATS = 5;
-  LID_SECURITY = 6;
-  LID_KERNEL = 7;
-}
-
-enum AndroidLogPriority {
-  PRIO_UNSPECIFIED = 0;
-  PRIO_UNUSED = 1;  // _DEFAULT, but should never be seen in logs.
-  PRIO_VERBOSE = 2;
-  PRIO_DEBUG = 3;
-  PRIO_INFO = 4;
-  PRIO_WARN = 5;
-  PRIO_ERROR = 6;
-  PRIO_FATAL = 7;
-}
-
-// End of protos/perfetto/common/android_log_constants.proto
-
-// Begin of protos/perfetto/common/data_source_descriptor.proto
-
-// This message is sent from Producer(s) to the tracing Service when registering
-// to advertise their capabilities. It describes the structure of tracing
-// protos that will be produced by the data source and the supported filters.
-message DataSourceDescriptor {
-  optional string name = 1;  // e.g., "linux.ftrace", "chromium.tracing"
-
-  // When true the data source is expected to ack the stop request through the
-  // NotifyDataSourceStopped() IPC. This field has been introduced after
-  // Android P in Jul 2018 and is not supported on older versions.
-  optional bool will_notify_on_stop = 2;
-
-  // When true the data source is expected to ack the start request through the
-  // NotifyDataSourceStarted() IPC. This field has been introduced after
-  // Android P in March 2019 and is not supported on older versions.
-  optional bool will_notify_on_start = 3;
-
-  // If true, opt into receiving the ClearIncrementalState() IPC. This should be
-  // set if the data source writes packets that refer to previous trace
-  // contents, and knows how to stop referring to the already-emitted data.
-  optional bool handles_incremental_state_clear = 4;
-
-  // Optional specification about available GPU counters.
-  optional GpuCounterDescriptor gpu_counter_descriptor = 5 [lazy = true];
-
-  optional TrackEventDescriptor track_event_descriptor = 6 [lazy = true];
-}
-
-// End of protos/perfetto/common/data_source_descriptor.proto
-
 // Begin of protos/perfetto/common/gpu_counter_descriptor.proto
 
 // Description of GPU counters.
@@ -178,6 +120,536 @@
 
 // End of protos/perfetto/common/gpu_counter_descriptor.proto
 
+// Begin of protos/perfetto/common/track_event_descriptor.proto
+
+message TrackEventCategory {
+  optional string name = 1;
+  optional string description = 2;
+  repeated string tags = 3;
+}
+
+message TrackEventDescriptor {
+  repeated TrackEventCategory available_categories = 1;
+}
+
+// End of protos/perfetto/common/track_event_descriptor.proto
+
+// Begin of protos/perfetto/common/data_source_descriptor.proto
+
+// This message is sent from Producer(s) to the tracing Service when registering
+// to advertise their capabilities. It describes the structure of tracing
+// protos that will be produced by the data source and the supported filters.
+message DataSourceDescriptor {
+  optional string name = 1;  // e.g., "linux.ftrace", "chromium.tracing"
+
+  // When true the data source is expected to ack the stop request through the
+  // NotifyDataSourceStopped() IPC. This field has been introduced after
+  // Android P in Jul 2018 and is not supported on older versions.
+  optional bool will_notify_on_stop = 2;
+
+  // When true the data source is expected to ack the start request through the
+  // NotifyDataSourceStarted() IPC. This field has been introduced after
+  // Android P in March 2019 and is not supported on older versions.
+  optional bool will_notify_on_start = 3;
+
+  // If true, opt into receiving the ClearIncrementalState() IPC. This should be
+  // set if the data source writes packets that refer to previous trace
+  // contents, and knows how to stop referring to the already-emitted data.
+  optional bool handles_incremental_state_clear = 4;
+
+  // Optional specification about available GPU counters.
+  optional GpuCounterDescriptor gpu_counter_descriptor = 5 [lazy = true];
+
+  optional TrackEventDescriptor track_event_descriptor = 6 [lazy = true];
+}
+
+// End of protos/perfetto/common/data_source_descriptor.proto
+
+// Begin of protos/perfetto/common/tracing_service_state.proto
+
+// Reports the state of the tracing service. Used to gather details about the
+// data sources connected.
+// See ConsumerPort::QueryServiceState().
+message TracingServiceState {
+  // Describes a producer process.
+  message Producer {
+    optional int32 id = 1;     // Unique ID of the producer (monotonic counter).
+    optional string name = 2;  // Typically matches the process name.
+    optional int32 uid = 3;    // Unix uid of the remote process.
+  }
+
+  // Describes a data source registered by a producer. Data sources are listed
+  // regardless of the fact that they are being used or not.
+  message DataSource {
+    // Descriptor passed by the data source when calling RegisterDataSource().
+    optional DataSourceDescriptor ds_descriptor = 1;
+
+    // ID of the producer, as per Producer.id.
+    optional int32 producer_id = 2;
+  }
+
+  // Lists all the producers connected.
+  repeated Producer producers = 1;
+
+  // Lists the data sources available.
+  repeated DataSource data_sources = 2;
+
+  // Total number of tracing sessions.
+  optional int32 num_sessions = 3;
+
+  // Number of tracing sessions in the started state. Always <= num_sessions.
+  optional int32 num_sessions_started = 4;
+}
+
+// End of protos/perfetto/common/tracing_service_state.proto
+
+// Begin of protos/perfetto/common/android_log_constants.proto
+
+// Values from NDK's android/log.h.
+enum AndroidLogId {
+  LID_DEFAULT = 0;  // MAIN.
+  LID_RADIO = 1;
+  LID_EVENTS = 2;
+  LID_SYSTEM = 3;
+  LID_CRASH = 4;
+  LID_STATS = 5;
+  LID_SECURITY = 6;
+  LID_KERNEL = 7;
+}
+
+enum AndroidLogPriority {
+  PRIO_UNSPECIFIED = 0;
+  PRIO_UNUSED = 1;  // _DEFAULT, but should never be seen in logs.
+  PRIO_VERBOSE = 2;
+  PRIO_DEBUG = 3;
+  PRIO_INFO = 4;
+  PRIO_WARN = 5;
+  PRIO_ERROR = 6;
+  PRIO_FATAL = 7;
+}
+
+// End of protos/perfetto/common/android_log_constants.proto
+
+// Begin of protos/perfetto/config/android/android_log_config.proto
+
+message AndroidLogConfig {
+  repeated AndroidLogId log_ids = 1;
+
+  reserved 2;  // Was |poll_ms|, deprecated.
+
+  // If set ignores all log messages whose prio is < the given value.
+  optional AndroidLogPriority min_prio = 3;
+
+  // If non-empty ignores all log messages whose tag doesn't match one of the
+  // specified values.
+  repeated string filter_tags = 4;
+}
+
+// End of protos/perfetto/config/android/android_log_config.proto
+
+// Begin of protos/perfetto/config/android/packages_list_config.proto
+
+// Data source that lists details (such as version code) about packages on an
+// Android device.
+message PackagesListConfig {
+  // If not empty, emit info about only the following list of package names
+  // (exact match, no regex). Otherwise, emit info about all packages.
+  repeated string package_name_filter = 1;
+}
+
+// End of protos/perfetto/config/android/packages_list_config.proto
+
+// Begin of protos/perfetto/config/chrome/chrome_config.proto
+
+message ChromeConfig {
+  optional string trace_config = 1;
+
+  // When enabled, the data source should only fill in fields in the output that
+  // are not potentially privacy sensitive.
+  optional bool privacy_filtering_enabled = 2;
+}
+
+// End of protos/perfetto/config/chrome/chrome_config.proto
+
+// Begin of protos/perfetto/config/ftrace/ftrace_config.proto
+
+message FtraceConfig {
+  repeated string ftrace_events = 1;
+  repeated string atrace_categories = 2;
+  repeated string atrace_apps = 3;
+  // *Per-CPU* buffer size.
+  optional uint32 buffer_size_kb = 10;
+  optional uint32 drain_period_ms = 11;
+
+  // Configuration for compact encoding of scheduler events. When enabled (and
+  // recording the relevant ftrace events), specific high-volume events are
+  // encoded in a denser format than normal.
+  message CompactSchedConfig {
+    // If true, and sched_switch or sched_waking ftrace events are enabled,
+    // record those events in the compact format.
+    optional bool enabled = 1;
+  }
+  optional CompactSchedConfig compact_sched = 12;
+}
+
+// End of protos/perfetto/config/ftrace/ftrace_config.proto
+
+// Begin of protos/perfetto/config/gpu/gpu_counter_config.proto
+
+message GpuCounterConfig {
+  // Desired sampling interval for counters.
+  optional uint64 counter_period_ns = 1;
+
+  // List of counters to be sampled. Counter IDs correspond to the ones
+  // described in GpuCounterSpec in the data source descriptor.
+  repeated uint32 counter_ids = 2;
+
+  // Sample counters by instrumenting command buffers.
+  optional bool instrumented_sampling = 3;
+
+  // Fix gpu clock rate during trace session.
+  optional bool fix_gpu_clock = 4;
+}
+
+// End of protos/perfetto/config/gpu/gpu_counter_config.proto
+
+// Begin of protos/perfetto/config/gpu/vulkan_memory_config.proto
+
+message VulkanMemoryConfig {
+  // Tracking driver memory usage events
+  optional bool track_driver_memory_usage = 1;
+
+  // Tracking device memory usage events
+  optional bool track_device_memory_usage = 2;
+}
+
+// End of protos/perfetto/config/gpu/vulkan_memory_config.proto
+
+// Begin of protos/perfetto/config/inode_file/inode_file_config.proto
+
+message InodeFileConfig {
+  message MountPointMappingEntry {
+    optional string mountpoint = 1;
+    repeated string scan_roots = 2;
+  }
+
+  // How long to pause between batches.
+  optional uint32 scan_interval_ms = 1;
+
+  // How long to wait before the first scan in order to accumulate inodes.
+  optional uint32 scan_delay_ms = 2;
+
+  // How many inodes to scan in one batch.
+  optional uint32 scan_batch_size = 3;
+
+  // Do not scan for inodes not found in the static map.
+  optional bool do_not_scan = 4;
+
+  // If non-empty, only scan inodes corresponding to block devices named in
+  // this list.
+  repeated string scan_mount_points = 5;
+
+  // When encountering an inode belonging to a block device corresponding
+  // to one of the mount points in this map, scan its scan_roots instead.
+  repeated MountPointMappingEntry mount_point_mapping = 6;
+}
+
+// End of protos/perfetto/config/inode_file/inode_file_config.proto
+
+// Begin of protos/perfetto/config/power/android_power_config.proto
+
+message AndroidPowerConfig {
+  enum BatteryCounters {
+    BATTERY_COUNTER_UNSPECIFIED = 0;
+    BATTERY_COUNTER_CHARGE = 1;            // Coulomb counter.
+    BATTERY_COUNTER_CAPACITY_PERCENT = 2;  // Charge (%).
+    BATTERY_COUNTER_CURRENT = 3;           // Instantaneous current.
+    BATTERY_COUNTER_CURRENT_AVG = 4;       // Avg current.
+  }
+  optional uint32 battery_poll_ms = 1;
+  repeated BatteryCounters battery_counters = 2;
+
+  // Where available enables per-power-rail measurements.
+  optional bool collect_power_rails = 3;
+}
+
+// End of protos/perfetto/config/power/android_power_config.proto
+
+// Begin of protos/perfetto/config/process_stats/process_stats_config.proto
+
+message ProcessStatsConfig {
+  enum Quirks {
+    QUIRKS_UNSPECIFIED = 0;
+
+    // This has been deprecated and ignored as per 2018-05-01. Full scan at
+    // startup is now disabled by default and can be re-enabled using the
+    // |scan_all_processes_on_start| arg.
+    DISABLE_INITIAL_DUMP = 1 [deprecated = true];
+
+    DISABLE_ON_DEMAND = 2;
+  }
+
+  repeated Quirks quirks = 1;
+
+  // If enabled all processes will be scanned and dumped when the trace starts.
+  optional bool scan_all_processes_on_start = 2;
+
+  // If enabled thread names are also recoded (this is redundant if sched_switch
+  // is enabled).
+  optional bool record_thread_names = 3;
+
+  // If > 0 samples counters (see process_stats.proto) from
+  // /proc/pid/status and oom_score_adj every X ms.
+  // This is required to be > 100ms to avoid excessive CPU usage.
+  // TODO(primiano): add CPU cost for change this value.
+  optional uint32 proc_stats_poll_ms = 4;
+
+  // If empty samples stats for all processes. If non empty samples stats only
+  // for processes matching the given string in their argv0 (i.e. the first
+  // entry of /proc/pid/cmdline).
+  // TODO(primiano): implement this feature.
+  // repeated string proc_stats_filter = 5;
+
+  // This is required to be either = 0 or a multiple of |proc_stats_poll_ms|
+  // (default: |proc_stats_poll_ms|). If = 0, will be set to
+  // |proc_stats_poll_ms|. Non-multiples will be rounded down to the nearest
+  // multiple.
+  optional uint32 proc_stats_cache_ttl_ms = 6;
+
+  // Whether to record /proc/tid/time_in_state.
+  optional bool record_thread_time_in_state = 7;
+
+  // Size of the cache for thread time_in_state cpu freq values.
+  // If not specificed, the default is used.
+  optional uint32 thread_time_in_state_cache_size = 8;
+}
+
+// End of protos/perfetto/config/process_stats/process_stats_config.proto
+
+// Begin of protos/perfetto/config/profiling/heapprofd_config.proto
+
+// Configuration for go/heapprofd.
+// Next id: 19
+message HeapprofdConfig {
+  message ContinuousDumpConfig {
+    // ms to wait before first dump.
+    optional uint32 dump_phase_ms = 5;
+    // ms to wait between following dumps.
+    optional uint32 dump_interval_ms = 6;
+  }
+
+  // Set to 1 for perfect accuracy.
+  // Otherwise, sample every sample_interval_bytes on average.
+  //
+  // See https://docs.perfetto.dev/#/heapprofd?id=sampling-interval for more
+  // details.
+  optional uint64 sampling_interval_bytes = 1;
+
+  // E.g. surfaceflinger, com.android.phone
+  // This input is normalized in the following way: if it contains slashes,
+  // everything up to the last slash is discarded. If it contains "@",
+  // everything after the first @ is discared.
+  // E.g. /system/bin/surfaceflinger@1.0 normalizes to surfaceflinger.
+  // This transformation is also applied to the processes' command lines when
+  // matching.
+  repeated string process_cmdline = 2;
+
+  // For watermark based triggering or local debugging.
+  repeated uint64 pid = 4;
+
+  // Profile all processes eligible for profiling on the system.
+  // See https://docs.perfetto.dev/#/heapprofd?id=target-processes for which
+  // processes are eligible.
+  //
+  // On unmodified userdebug builds, this will lead to system crashes. Zygote
+  // will crash when trying to launch a new process as it will have an
+  // unexpected open socket to heapprofd.
+  //
+  // heapprofd will likely be overloaded by the amount of data for low
+  // sampling intervals.
+  optional bool all = 5;
+
+  // Do not profile processes whose anon RSS + swap < given value.
+  optional uint32 min_anonymous_memory_kb = 15;
+
+  // Stop profile if heapprofd memory usage goes beyond the given value.
+  optional uint32 max_heapprofd_memory_kb = 16;
+
+  // Stop profile if heapprofd CPU time since start of this data-source
+  // goes beyond given value.
+  optional uint64 max_heapprofd_cpu_secs = 17;
+
+  // Do not emit function names for mappings starting with this prefix.
+  // E.g. /system to not emit symbols for any system libraries.
+  repeated string skip_symbol_prefix = 7;
+
+  // Dump at a predefined interval.
+  optional ContinuousDumpConfig continuous_dump_config = 6;
+
+  // Size of the shared memory buffer between the profiled processes and
+  // heapprofd. Defaults to 8 MiB. If larger than 500 MiB, truncated to 500
+  // MiB.
+  //
+  // Needs to be:
+  // * at least 8192,
+  // * a power of two,
+  // * a multiple of 4096.
+  optional uint64 shmem_size_bytes = 8;
+
+  // When the shmem buffer is full, block the client instead of ending the
+  // trace. Use with caution as this will significantly slow down the target
+  // process.
+  optional bool block_client = 9;
+
+  // If set, stop the trace session after blocking the client for this
+  // timeout. Needs to be larger than 100 us, otherwise no retries are done.
+  optional uint32 block_client_timeout_us = 14;
+
+  // Do not profile processes from startup, only match already running
+  // processes.
+  //
+  // Can not be set at the same time as no_running.
+  optional bool no_startup = 10;
+
+  // Do not profile running processes. Only match processes on startup.
+  //
+  // Can not be set at the same time as no_startup.
+  optional bool no_running = 11;
+
+  // Gather information on how many bytes of allocations are on non-referenced
+  // pages. The way to use this generally is:
+  // 1. Start profile of app.
+  // 2. Start app.
+  // 3. Trigger a dump by sending SIGUSR1 to heapprofd.
+  // 4. Do operations.
+  // 5. End profile.
+  //
+  // You can then find the allocations that were not used for the operations you
+  // did in step 4.
+  optional bool idle_allocations = 12;
+
+  // Cause heapprofd to emit a single dump at the end, showing the memory usage
+  // at the point in time when the sampled heap usage of the process was at its
+  // maximum. This causes ProfilePacket.HeapSample.self_max to be set, and
+  // self_allocated and self_freed to not be set.
+  optional bool dump_at_max = 13;
+
+  // FEATURE FLAGS. THERE BE DRAGONS.
+
+  // Escape hatch if the session is being torn down because of a forked child
+  // that shares memory space, but is not correctly identified as a vforked
+  // child.
+  optional bool disable_fork_teardown = 18;
+}
+
+// End of protos/perfetto/config/profiling/heapprofd_config.proto
+
+// Begin of protos/perfetto/config/profiling/java_hprof_config.proto
+
+// Configuration for go/heapprofd.
+message JavaHprofConfig {
+  // If dump_interval_ms != 0, the following configuration is used.
+  message ContinuousDumpConfig {
+    // ms to wait before first continuous dump.
+    // A dump is always created at the beginning of the trace.
+    optional uint32 dump_phase_ms = 1;
+    // ms to wait between following dumps.
+    optional uint32 dump_interval_ms = 2;
+  }
+
+  // This input is normalized in the following way: if it contains slashes,
+  // everything up to the last slash is discarded. If it contains "@",
+  // everything after the first @ is discared.
+  // E.g. /system/bin/surfaceflinger@1.0 normalizes to surfaceflinger.
+  // This transformation is also applied to the processes' command lines when
+  // matching.
+  repeated string process_cmdline = 1;
+
+  // For watermark based triggering or local debugging.
+  repeated uint64 pid = 2;
+
+  // Dump at a predefined interval.
+  optional ContinuousDumpConfig continuous_dump_config = 3;
+
+  // Do not profile processes whose anon RSS + swap < given value.
+  optional uint32 min_anonymous_memory_kb = 4;
+
+  // Include the process' /proc/self/smaps.
+  // This only shows maps that:
+  // * start with /system
+  // * start with /vendor
+  // * start with /data/app
+  // * contain "extracted in memory from Y", where Y matches any of the above
+  optional bool dump_smaps = 5;
+}
+
+// End of protos/perfetto/config/profiling/java_hprof_config.proto
+
+// Begin of protos/perfetto/config/profiling/perf_event_config.proto
+
+// Configuration for the traced_perf profiler.
+//
+// At the time of writing, the config options are restricted to the periodic
+// system-wide stack sampling use-case (|all_cpus| must be true).
+message PerfEventConfig {
+  // If true, sample events on all CPUs.
+  optional bool all_cpus = 1;
+
+  // Per-cpu sampling frequency (requested from the kernel). Not guaranteed to
+  // be honored as the kernel can throttle the sampling rate if it's too high.
+  // If unset, an implementation-defined default is used.
+  optional uint32 sampling_frequency = 2;
+
+  // How often the per-cpu ring buffers are read by the producer.
+  // If unset, an implementation-defined default is used.
+  optional uint32 ring_buffer_read_period_ms = 8;
+
+  // Size (in 4k pages) of each per-cpu ring buffer that is filled by the
+  // kernel. If set, must be a power of two.
+  // If unset, an implementation-defined default is used.
+  optional uint32 ring_buffer_pages = 3;
+
+  // Process ID (TGID) whitelist. If this list is not empty, only matching
+  // samples will be retained. If multiple whitelists and blacklists are
+  // specified by the config, then all of them are evaluated for each sampled
+  // process.
+  repeated int32 target_pid = 4;
+
+  // Command line whitelist, matched against the
+  // /proc/<pid>/cmdline (not the comm string), with both sides being
+  // "normalized". Normalization is as follows: (1) trim everything beyond the
+  // first null or "@" byte; (2) if the string contains forward slashes, trim
+  // everything up to and including the last one.
+  repeated string target_cmdline = 5;
+
+  // PID blacklist.
+  repeated int32 exclude_pid = 6;
+
+  // Command line blacklist. Normalized in the same way as |target_cmdline|.
+  repeated string exclude_cmdline = 7;
+
+  ////////////////////
+  // Uncommon options:
+
+  // Timeout for the remote /proc/<pid>/{maps,mem} file descriptors for a
+  // sampled process. This is primarily for Android, where this lookup is
+  // asynchronous. As long as the producer is waiting, the associated samples
+  // will be kept enqueued (putting pressure on the capacity of the shared
+  // unwinding queue). Once a lookup for a process expires, all associated
+  // samples are discarded. However, if the lookup still succeeds after the
+  // timeout, future samples will be handled normally.
+  // If unset, an implementation-defined default is used.
+  optional uint32 remote_descriptor_timeout_ms = 9;
+
+  // Optional period for clearing state cached by the unwinder. This is a heavy
+  // operation that is only necessary for traces that target a wide set of
+  // processes, and require the memory footprint to be reset periodically.
+  // If unset, the cached state will not be cleared.
+  optional uint32 unwind_state_clear_period_ms = 10;
+}
+
+// End of protos/perfetto/config/profiling/perf_event_config.proto
+
 // Begin of protos/perfetto/common/sys_stats_counters.proto
 
 // When editing entries here remember also to update "sys_stats_counters.h" with
@@ -322,437 +794,6 @@
 }
 // End of protos/perfetto/common/sys_stats_counters.proto
 
-// Begin of protos/perfetto/common/trace_stats.proto
-
-// Statistics for the internals of the tracing service.
-//
-// Next id: 11.
-message TraceStats {
-  // From TraceBuffer::Stats.
-  //
-  // Next id: 20.
-  message BufferStats {
-    // Size of the circular buffer in bytes.
-    optional uint64 buffer_size = 12;
-
-    // Num. bytes written into the circular buffer, including chunk headers.
-    optional uint64 bytes_written = 1;
-
-    // Num. bytes overwritten before they have been read (i.e. loss of data).
-    optional uint64 bytes_overwritten = 13;
-
-    // Total size of chunks that were fully read from the circular buffer by the
-    // consumer. This may not be equal to |bytes_written| either in the middle
-    // of tracing, or if |chunks_overwritten| is non-zero. Note that this is the
-    // size of the chunks read from the buffer, including chunk headers, which
-    // will be different from the total size of packets returned to the
-    // consumer.
-    //
-    // The current utilization of the trace buffer (mid-tracing) can be obtained
-    // by subtracting |bytes_read| and |bytes_overwritten| from |bytes_written|,
-    // adding the difference of |padding_bytes_written| and
-    // |padding_bytes_cleared|, and comparing this sum to the |buffer_size|.
-    // Note that this represents the total size of buffered data in the buffer,
-    // yet this data may be spread non-contiguously through the buffer and may
-    // be overridden before the utilization reaches 100%.
-    optional uint64 bytes_read = 14;
-
-    // Num. bytes that were allocated as padding between chunks in the circular
-    // buffer.
-    optional uint64 padding_bytes_written = 15;
-
-    // Num. of padding bytes that were removed from the circular buffer when
-    // they were overwritten.
-    //
-    // The difference between |padding_bytes_written| and
-    // |padding_bytes_cleared| denotes the total size of padding currently
-    // present in the buffer.
-    optional uint64 padding_bytes_cleared = 16;
-
-    // Num. chunks (!= packets) written into the buffer.
-    optional uint64 chunks_written = 2;
-
-    // Num. chunks (!= packets) rewritten into the buffer. This means we rewrote
-    // the same chunk with additional packets appended to the end.
-    optional uint64 chunks_rewritten = 10;
-
-    // Num. chunks overwritten before they have been read (i.e. loss of data).
-    optional uint64 chunks_overwritten = 3;
-
-    // Num. chunks discarded (i.e. loss of data). Can be > 0 only when a buffer
-    // is configured with FillPolicy == DISCARD.
-    optional uint64 chunks_discarded = 18;
-
-    // Num. chunks (!= packets) that were fully read from the circular buffer by
-    // the consumer. This may not be equal to |chunks_written| either in the
-    // middle of tracing, or if |chunks_overwritten| is non-zero.
-    optional uint64 chunks_read = 17;
-
-    // Num. chunks that were committed out of order.
-    optional uint64 chunks_committed_out_of_order = 11;
-
-    // Num. times the ring buffer wrapped around.
-    optional uint64 write_wrap_count = 4;
-
-    // Num. out-of-band (OOB) patches that succeeded.
-    optional uint64 patches_succeeded = 5;
-
-    // Num. OOB patches that failed (e.g., the chunk to patch was gone).
-    optional uint64 patches_failed = 6;
-
-    // Num. readaheads (for large multi-chunk packet reads) that ended up in a
-    // successful packet read.
-    optional uint64 readaheads_succeeded = 7;
-
-    // Num. readaheads aborted because of missing chunks in the sequence stream.
-    // Note that a small number > 0 is totally expected: occasionally, when
-    // issuing a read, the very last packet in a sequence might be incomplete
-    // (because the producer is still writing it while we read). The read will
-    // stop at that point, for that sequence, increasing this counter.
-    optional uint64 readaheads_failed = 8;
-
-    // Num. of violations of the SharedMemoryABI found while writing or reading
-    // the buffer. This is an indication of either a bug in the producer(s) or
-    // malicious producer(s).
-    optional uint64 abi_violations = 9;
-
-    // The fields below have been introduced in Android R.
-
-    // Num. of times the service detected packet loss on a trace writer
-    // sequence. This is usually caused by exhaustion of available chunks in the
-    // writer process's SMB. Note that this relies on the client's TraceWriter
-    // indicating this loss to the service -- packets lost for other reasons are
-    // not reflected in this stat.
-    optional uint64 trace_writer_packet_loss = 19;
-  }
-
-  // Stats for the TraceBuffer(s) of the current trace session.
-  repeated BufferStats buffer_stats = 1;
-
-  // Num. producers connected (whether they are involved in the current tracing
-  // session or not).
-  optional uint32 producers_connected = 2;
-
-  // Num. producers ever seen for all trace sessions since startup (it's a good
-  // proxy for inferring num. producers crashed / killed).
-  optional uint64 producers_seen = 3;
-
-  // Num. data sources registered for all trace sessions.
-  optional uint32 data_sources_registered = 4;
-
-  // Num. data sources ever seen for all trace sessions since startup.
-  optional uint64 data_sources_seen = 5;
-
-  // Num. concurrently active tracing sessions.
-  optional uint32 tracing_sessions = 6;
-
-  // Num. buffers for all tracing session (not just the current one). This will
-  // be >= buffer_stats.size(), because the latter is only about the current
-  // session.
-  optional uint32 total_buffers = 7;
-
-  // The fields below have been introduced in Android Q.
-
-  // Num. chunks that were discarded by the service before attempting to commit
-  // them to a buffer, e.g. because the producer specified an invalid buffer ID.
-  optional uint64 chunks_discarded = 8;
-
-  // Num. patches that were discarded by the service before attempting to apply
-  // them to a buffer, e.g. because the producer specified an invalid buffer ID.
-  optional uint64 patches_discarded = 9;
-
-  // Packets that failed validation of the TrustedPacket. If this is > 0, there
-  // is a bug in the producer.
-  optional uint64 invalid_packets = 10;
-}
-
-// End of protos/perfetto/common/trace_stats.proto
-
-// Begin of protos/perfetto/common/tracing_service_state.proto
-
-// Reports the state of the tracing service. Used to gather details about the
-// data sources connected.
-// See ConsumerPort::QueryServiceState().
-message TracingServiceState {
-  // Describes a producer process.
-  message Producer {
-    optional int32 id = 1;     // Unique ID of the producer (monotonic counter).
-    optional string name = 2;  // Typically matches the process name.
-    optional int32 uid = 3;    // Unix uid of the remote process.
-  }
-
-  // Describes a data source registered by a producer. Data sources are listed
-  // regardless of the fact that they are being used or not.
-  message DataSource {
-    // Descriptor passed by the data source when calling RegisterDataSource().
-    optional DataSourceDescriptor ds_descriptor = 1;
-
-    // ID of the producer, as per Producer.id.
-    optional int32 producer_id = 2;
-  }
-
-  // Lists all the producers connected.
-  repeated Producer producers = 1;
-
-  // Lists the data sources available.
-  repeated DataSource data_sources = 2;
-
-  // Total number of tracing sessions.
-  optional int32 num_sessions = 3;
-
-  // Number of tracing sessions in the started state. Always <= num_sessions.
-  optional int32 num_sessions_started = 4;
-}
-
-// End of protos/perfetto/common/tracing_service_state.proto
-
-// Begin of protos/perfetto/common/track_event_descriptor.proto
-
-message TrackEventDescriptor {
-  repeated string available_categories = 1;
-}
-
-// End of protos/perfetto/common/track_event_descriptor.proto
-
-// Begin of protos/perfetto/config/android/android_log_config.proto
-
-message AndroidLogConfig {
-  repeated AndroidLogId log_ids = 1;
-
-  reserved 2;  // Was |poll_ms|, deprecated.
-
-  // If set ignores all log messages whose prio is < the given value.
-  optional AndroidLogPriority min_prio = 3;
-
-  // If non-empty ignores all log messages whose tag doesn't match one of the
-  // specified values.
-  repeated string filter_tags = 4;
-}
-
-// End of protos/perfetto/config/android/android_log_config.proto
-
-// Begin of protos/perfetto/config/chrome/chrome_config.proto
-
-message ChromeConfig {
-  optional string trace_config = 1;
-
-  // When enabled, the data source should only fill in fields in the output that
-  // are not potentially privacy sensitive.
-  optional bool privacy_filtering_enabled = 2;
-}
-
-// End of protos/perfetto/config/chrome/chrome_config.proto
-
-// Begin of protos/perfetto/config/data_source_config.proto
-
-// The configuration that is passed to each data source when starting tracing.
-message DataSourceConfig {
-  // Data source unique name, e.g., "linux.ftrace". This must match
-  // the name passed by the data source when it registers (see
-  // RegisterDataSource()).
-  optional string name = 1;
-
-  // The index of the logging buffer where TracePacket(s) will be stored.
-  // This field doesn't make a major difference for the Producer(s). The final
-  // logging buffers, in fact, are completely owned by the Service. We just ask
-  // the Producer to copy this number into the chunk headers it emits, so that
-  // the Service can quickly identify the buffer where to move the chunks into
-  // without expensive lookups on its fastpath.
-  optional uint32 target_buffer = 2;
-
-  // Set by the service to indicate the duration of the trace.
-  // DO NOT SET in consumer as this will be overridden by the service.
-  optional uint32 trace_duration_ms = 3;
-
-  // Set by the service to indicate how long it waits after StopDataSource.
-  // DO NOT SET in consumer as this will be overridden by the service.
-  optional uint32 stop_timeout_ms = 7;
-
-  // Set by the service to indicate whether this tracing session has extra
-  // guardrails.
-  // DO NOT SET in consumer as this will be overridden by the service.
-  optional bool enable_extra_guardrails = 6;
-
-  // Set by the service to indicate which tracing session the data source
-  // belongs to. The intended use case for this is checking if two data sources,
-  // one of which produces metadata for the other one, belong to the same trace
-  // session and hence should be linked together.
-  // This field was introduced in Aug 2018 after Android P.
-  optional uint64 tracing_session_id = 4;
-
-  // Keeep the lower IDs (up to 99) for fields that are *not* specific to
-  // data-sources and needs to be processed by the traced daemon.
-
-  // All data source config fields must be marked as [lazy=true]. This prevents
-  // the proto-to-cpp generator from recursing into those when generating the
-  // cpp classes and polluting tracing/core with data-source-specific classes.
-  // Instead they are treated as opaque strings containing raw proto bytes.
-
-  // Data source name: linux.ftrace
-  optional FtraceConfig ftrace_config = 100 [lazy = true];
-  // Data source name: linux.inode_file_map
-  optional InodeFileConfig inode_file_config = 102 [lazy = true];
-  // Data source name: linux.process_stats
-  optional ProcessStatsConfig process_stats_config = 103 [lazy = true];
-  // Data source name: linux.sys_stats
-  optional SysStatsConfig sys_stats_config = 104 [lazy = true];
-  // Data source name: android.heapprofd
-  optional HeapprofdConfig heapprofd_config = 105 [lazy = true];
-  // Data source name: android.java_hprof
-  optional JavaHprofConfig java_hprof_config = 110 [lazy = true];
-  // Data source name: android.power
-  optional AndroidPowerConfig android_power_config = 106 [lazy = true];
-  // Data source name: android.log
-  optional AndroidLogConfig android_log_config = 107 [lazy = true];
-  // TODO(fmayer): Add data source name for this.
-  optional GpuCounterConfig gpu_counter_config = 108 [lazy = true];
-  // Data source name: android.packages_list
-  optional PackagesListConfig packages_list_config = 109 [lazy = true];
-  // Data source name: linux.perf
-  optional PerfEventConfig perf_event_config = 111 [lazy = true];
-  // Data source name: vulkan.memory_tracker
-  optional VulkanMemoryConfig vulkan_memory_config = 112 [lazy = true];
-
-  // Chrome is special as it doesn't use the perfetto IPC layer. We want to
-  // avoid proto serialization and de-serialization there because that would
-  // just add extra hops on top of the Mojo ser/des. Instead we auto-generate a
-  // C++ class for it so it can pass around plain C++ objets.
-  optional ChromeConfig chrome_config = 101;
-
-  // This is a fallback mechanism to send a free-form text config to the
-  // producer. In theory this should never be needed. All the code that
-  // is part of the platform (i.e. traced service) is supposed to *not* truncate
-  // the trace config proto and propagate unknown fields. However, if anything
-  // in the pipeline (client or backend) ends up breaking this forward compat
-  // plan, this field will become the escape hatch to allow future data sources
-  // to get some meaningful configuration.
-  optional string legacy_config = 1000;
-
-  // This field is only used for testing.
-  optional TestConfig for_testing = 1001;
-
-  reserved 268435455;  // Was |for_testing|. Caused more problems then found.
-}
-
-// End of protos/perfetto/config/data_source_config.proto
-
-// Begin of protos/perfetto/config/ftrace/ftrace_config.proto
-
-message FtraceConfig {
-  repeated string ftrace_events = 1;
-  repeated string atrace_categories = 2;
-  repeated string atrace_apps = 3;
-  // *Per-CPU* buffer size.
-  optional uint32 buffer_size_kb = 10;
-  optional uint32 drain_period_ms = 11;
-
-  // Configuration for compact encoding of scheduler events. When enabled (and
-  // recording the relevant ftrace events), specific high-volume events are
-  // encoded in a denser format than normal.
-  message CompactSchedConfig {
-    // If true, and sched_switch or sched_waking ftrace events are enabled,
-    // record those events in the compact format.
-    optional bool enabled = 1;
-  }
-  optional CompactSchedConfig compact_sched = 12;
-}
-
-// End of protos/perfetto/config/ftrace/ftrace_config.proto
-
-// Begin of protos/perfetto/config/inode_file/inode_file_config.proto
-
-message InodeFileConfig {
-  message MountPointMappingEntry {
-    optional string mountpoint = 1;
-    repeated string scan_roots = 2;
-  }
-
-  // How long to pause between batches.
-  optional uint32 scan_interval_ms = 1;
-
-  // How long to wait before the first scan in order to accumulate inodes.
-  optional uint32 scan_delay_ms = 2;
-
-  // How many inodes to scan in one batch.
-  optional uint32 scan_batch_size = 3;
-
-  // Do not scan for inodes not found in the static map.
-  optional bool do_not_scan = 4;
-
-  // If non-empty, only scan inodes corresponding to block devices named in
-  // this list.
-  repeated string scan_mount_points = 5;
-
-  // When encountering an inode belonging to a block device corresponding
-  // to one of the mount points in this map, scan its scan_roots instead.
-  repeated MountPointMappingEntry mount_point_mapping = 6;
-}
-
-// End of protos/perfetto/config/inode_file/inode_file_config.proto
-
-// Begin of protos/perfetto/config/power/android_power_config.proto
-
-message AndroidPowerConfig {
-  enum BatteryCounters {
-    BATTERY_COUNTER_UNSPECIFIED = 0;
-    BATTERY_COUNTER_CHARGE = 1;            // Coulomb counter.
-    BATTERY_COUNTER_CAPACITY_PERCENT = 2;  // Charge (%).
-    BATTERY_COUNTER_CURRENT = 3;           // Instantaneous current.
-    BATTERY_COUNTER_CURRENT_AVG = 4;       // Avg current.
-  }
-  optional uint32 battery_poll_ms = 1;
-  repeated BatteryCounters battery_counters = 2;
-
-  // Where available enables per-power-rail measurements.
-  optional bool collect_power_rails = 3;
-}
-
-// End of protos/perfetto/config/power/android_power_config.proto
-
-// Begin of protos/perfetto/config/process_stats/process_stats_config.proto
-
-message ProcessStatsConfig {
-  enum Quirks {
-    QUIRKS_UNSPECIFIED = 0;
-
-    // This has been deprecated and ignored as per 2018-05-01. Full scan at
-    // startup is now disabled by default and can be re-enabled using the
-    // |scan_all_processes_on_start| arg.
-    DISABLE_INITIAL_DUMP = 1 [deprecated = true];
-
-    DISABLE_ON_DEMAND = 2;
-  }
-
-  repeated Quirks quirks = 1;
-
-  // If enabled all processes will be scanned and dumped when the trace starts.
-  optional bool scan_all_processes_on_start = 2;
-
-  // If enabled thread names are also recoded (this is redundant if sched_switch
-  // is enabled).
-  optional bool record_thread_names = 3;
-
-  // If > 0 samples counters (see process_stats.proto) from
-  // /proc/pid/status and oom_score_adj every X ms.
-  // This is required to be > 100ms to avoid excessive CPU usage.
-  // TODO(primiano): add CPU cost for change this value.
-  optional uint32 proc_stats_poll_ms = 4;
-
-  // If empty samples stats for all processes. If non empty samples stats only
-  // for processes matching the given string in their argv0 (i.e. the first
-  // entry of /proc/pid/cmdline).
-  // TODO(primiano): implement this feature.
-  // repeated string proc_stats_filter = 5;
-
-  // This is required to be either = 0 or a multiple of |proc_stats_poll_ms|
-  // (default: |proc_stats_poll_ms|). If = 0, will be set to
-  // |proc_stats_poll_ms|. Non-multiples will be rounded down to the nearest
-  // multiple.
-  optional uint32 proc_stats_cache_ttl_ms = 6;
-}
-
-// End of protos/perfetto/config/process_stats/process_stats_config.proto
-
 // Begin of protos/perfetto/config/sys_stats/sys_stats_config.proto
 
 // This file defines the configuration for the Linux /proc poller data source,
@@ -844,6 +885,145 @@
 
 // End of protos/perfetto/config/test_config.proto
 
+// Begin of protos/perfetto/config/track_event/track_event_config.proto
+
+message TrackEventConfig {
+  // The following fields define the set of enabled trace categories. Each list
+  // item is a glob.
+  //
+  // To determine if category is enabled, it is checked against the filters in
+  // the following order:
+  //
+  //   1. Exact matches in enabled categories.
+  //   2. Exact matches in enabled tags.
+  //   3. Exact matches in disabled categories.
+  //   4. Exact matches in disabled tags.
+  //   5. Pattern matches in enabled categories.
+  //   6. Pattern matches in enabled tags.
+  //   7. Pattern matches in disabled categories.
+  //   8. Pattern matches in disabled tags.
+  //
+  // If none of the steps produced a match, the category is enabled by default.
+  //
+  // Examples:
+  //
+  //  - To enable all non-slow/debug categories:
+  //
+  //       No configuration needed, happens by default.
+  //
+  //  - To enable a specific category:
+  //
+  //       disabled_categories = ["*"]
+  //       enabled_categories = ["my_category"]
+  //
+  //  - To enable only categories with a specific tag:
+  //
+  //       disabled_tags = ["*"]
+  //       enabled_tags = ["my_tag"]
+  //
+  repeated string disabled_categories = 1;  // Default: []
+  repeated string enabled_categories = 2;   // Default: []
+  repeated string disabled_tags = 3;  // Default: [“slow”, “debug”]
+  repeated string enabled_tags = 4;   // Default: []
+}
+
+// End of protos/perfetto/config/track_event/track_event_config.proto
+
+// Begin of protos/perfetto/config/data_source_config.proto
+
+// The configuration that is passed to each data source when starting tracing.
+message DataSourceConfig {
+  // Data source unique name, e.g., "linux.ftrace". This must match
+  // the name passed by the data source when it registers (see
+  // RegisterDataSource()).
+  optional string name = 1;
+
+  // The index of the logging buffer where TracePacket(s) will be stored.
+  // This field doesn't make a major difference for the Producer(s). The final
+  // logging buffers, in fact, are completely owned by the Service. We just ask
+  // the Producer to copy this number into the chunk headers it emits, so that
+  // the Service can quickly identify the buffer where to move the chunks into
+  // without expensive lookups on its fastpath.
+  optional uint32 target_buffer = 2;
+
+  // Set by the service to indicate the duration of the trace.
+  // DO NOT SET in consumer as this will be overridden by the service.
+  optional uint32 trace_duration_ms = 3;
+
+  // Set by the service to indicate how long it waits after StopDataSource.
+  // DO NOT SET in consumer as this will be overridden by the service.
+  optional uint32 stop_timeout_ms = 7;
+
+  // Set by the service to indicate whether this tracing session has extra
+  // guardrails.
+  // DO NOT SET in consumer as this will be overridden by the service.
+  optional bool enable_extra_guardrails = 6;
+
+  // Set by the service to indicate which tracing session the data source
+  // belongs to. The intended use case for this is checking if two data sources,
+  // one of which produces metadata for the other one, belong to the same trace
+  // session and hence should be linked together.
+  // This field was introduced in Aug 2018 after Android P.
+  optional uint64 tracing_session_id = 4;
+
+  // Keeep the lower IDs (up to 99) for fields that are *not* specific to
+  // data-sources and needs to be processed by the traced daemon.
+
+  // All data source config fields must be marked as [lazy=true]. This prevents
+  // the proto-to-cpp generator from recursing into those when generating the
+  // cpp classes and polluting tracing/core with data-source-specific classes.
+  // Instead they are treated as opaque strings containing raw proto bytes.
+
+  // Data source name: linux.ftrace
+  optional FtraceConfig ftrace_config = 100 [lazy = true];
+  // Data source name: linux.inode_file_map
+  optional InodeFileConfig inode_file_config = 102 [lazy = true];
+  // Data source name: linux.process_stats
+  optional ProcessStatsConfig process_stats_config = 103 [lazy = true];
+  // Data source name: linux.sys_stats
+  optional SysStatsConfig sys_stats_config = 104 [lazy = true];
+  // Data source name: android.heapprofd
+  optional HeapprofdConfig heapprofd_config = 105 [lazy = true];
+  // Data source name: android.java_hprof
+  optional JavaHprofConfig java_hprof_config = 110 [lazy = true];
+  // Data source name: android.power
+  optional AndroidPowerConfig android_power_config = 106 [lazy = true];
+  // Data source name: android.log
+  optional AndroidLogConfig android_log_config = 107 [lazy = true];
+  // TODO(fmayer): Add data source name for this.
+  optional GpuCounterConfig gpu_counter_config = 108 [lazy = true];
+  // Data source name: android.packages_list
+  optional PackagesListConfig packages_list_config = 109 [lazy = true];
+  // Data source name: linux.perf
+  optional PerfEventConfig perf_event_config = 111 [lazy = true];
+  // Data source name: vulkan.memory_tracker
+  optional VulkanMemoryConfig vulkan_memory_config = 112 [lazy = true];
+  // Data source name: track_event
+  optional TrackEventConfig track_event_config = 113 [lazy = true];
+
+  // Chrome is special as it doesn't use the perfetto IPC layer. We want to
+  // avoid proto serialization and de-serialization there because that would
+  // just add extra hops on top of the Mojo ser/des. Instead we auto-generate a
+  // C++ class for it so it can pass around plain C++ objets.
+  optional ChromeConfig chrome_config = 101;
+
+  // This is a fallback mechanism to send a free-form text config to the
+  // producer. In theory this should never be needed. All the code that
+  // is part of the platform (i.e. traced service) is supposed to *not* truncate
+  // the trace config proto and propagate unknown fields. However, if anything
+  // in the pipeline (client or backend) ends up breaking this forward compat
+  // plan, this field will become the escape hatch to allow future data sources
+  // to get some meaningful configuration.
+  optional string legacy_config = 1000;
+
+  // This field is only used for testing.
+  optional TestConfig for_testing = 1001;
+
+  reserved 268435455;  // Was |for_testing|. Caused more problems then found.
+}
+
+// End of protos/perfetto/config/data_source_config.proto
+
 // Begin of protos/perfetto/config/trace_config.proto
 
 // The overall config that is used when starting a new tracing session through
@@ -851,7 +1031,7 @@
 // It contains the general config for the logging buffer(s) and the configs for
 // all the data source being enabled.
 //
-// Next id: 29.
+// Next id: 30.
 message TraceConfig {
   message BufferConfig {
     optional uint32 size_kb = 1;
@@ -884,30 +1064,40 @@
     optional protos.DataSourceConfig config = 1;
 
     // Optional. If multiple producers (~processes) expose the same data source
-    // and |producer_name_filter| != "", the data source is enabled only for
-    // producers whose names match any of the producer_name_filter below.
-    // The |producer_name_filter| has to be an exact match. (TODO(primiano):
-    // support wildcards or regex).
+    // and either |producer_name_filter| or |producer_name_regex_filter| is set,
+    // the data source is enabled only for producers whose names match any of
+    // the filters.
+    // |producer_name_filter| has to be an exact match, while
+    // |producer_name_regex_filter| is a regular expression.
     // This allows to enable a data source only for specific processes.
-    // The "repeated" field has OR sematics: specifying a filter ["foo", "bar"]
-    // will enable data source on both "foo" and "bar" (if existent).
+    // The "repeated" fields have OR semantics: specifying a filter ["foo",
+    // "bar"] will enable data sources on both "foo" and "bar" (if they exist).
     repeated string producer_name_filter = 2;
+    repeated string producer_name_regex_filter = 3;
   }
   repeated DataSource data_sources = 2;
 
-  // Config for builtin trace packets emitted by perfetto like trace stats,
-  // system info, etc.
+  // Config for disabling builtin data sources in the tracing service.
   message BuiltinDataSource {
     // Disable emitting clock timestamps into the trace.
     optional bool disable_clock_snapshotting = 1;
 
+    // Disable echoing the original trace config in the trace.
     optional bool disable_trace_config = 2;
 
+    // Disable emitting system info (build fingerprint, cpuinfo, etc).
     optional bool disable_system_info = 3;
+
+    // Disable emitting events for data-source state changes (e.g. the marker
+    // for all data sources having ACKed the start of the trace).
+    optional bool disable_service_events = 4;
   }
   optional BuiltinDataSource builtin_data_sources = 20;
 
   // If specified, the trace will be stopped |duration_ms| after starting.
+  // This does *not* count the time the system is suspended, so we will run
+  // for duration_ms of system activity, not wall time.
+  //
   // However in case of traces with triggers, see
   // TriggerConfig.trigger_timeout_ms instead.
   optional uint32 duration_ms = 3;
@@ -959,11 +1149,21 @@
   // Statsd-specific metadata.
   optional StatsdMetadata statsd_metadata = 7;
 
-  // When true, the EnableTracing() request must also provide a file descriptor.
-  // The service will then periodically read packets out of the trace buffer and
-  // store it into the passed file.
+  // When true && |output_path| is empty, the EnableTracing() request must
+  // provide a file descriptor. The service will then periodically read packets
+  // out of the trace buffer and store it into the passed file.
+  // If |output_path| is not empty no fd should be passed, the service
+  // will create a new file and write into that (see comment below).
   optional bool write_into_file = 8;
 
+  // This must point to a non-existing file. If the file exists the service
+  // will NOT overwrite and will fail instead as a security precaution.
+  // On Android, when this is used with the system traced, the path must be
+  // within /data/misc/perfetto-traces/ or the trace will fail.
+  // This option has been introduced in Android R. Before R write_into_file
+  // can be used only with the "pass a file descriptor over IPC" mode.
+  optional string output_path = 29;
+
   // Optional. If non-zero tunes the write period. A min value of 100ms is
   // enforced (i.e. smaller values are ignored).
   optional uint32 file_write_period_ms = 9;
@@ -1136,185 +1336,3 @@
 }
 
 // End of protos/perfetto/config/trace_config.proto
-
-// Begin of protos/perfetto/config/profiling/heapprofd_config.proto
-
-// Configuration for go/heapprofd.
-message HeapprofdConfig {
-  message ContinuousDumpConfig {
-    // ms to wait before first dump.
-    optional uint32 dump_phase_ms = 5;
-    // ms to wait between following dumps.
-    optional uint32 dump_interval_ms = 6;
-  }
-
-  // Set to 1 for perfect accuracy.
-  // Otherwise, sample every sample_interval_bytes on average.
-  //
-  // See https://docs.perfetto.dev/#/heapprofd?id=sampling-interval for more
-  // details.
-  optional uint64 sampling_interval_bytes = 1;
-
-  // E.g. surfaceflinger, com.android.phone
-  // This input is normalized in the following way: if it contains slashes,
-  // everything up to the last slash is discarded. If it contains "@",
-  // everything after the first @ is discared.
-  // E.g. /system/bin/surfaceflinger@1.0 normalizes to surfaceflinger.
-  // This transformation is also applied to the processes' command lines when
-  // matching.
-  repeated string process_cmdline = 2;
-
-  // For watermark based triggering or local debugging.
-  repeated uint64 pid = 4;
-
-  // Profile all processes eligible for profiling on the system.
-  // See https://docs.perfetto.dev/#/heapprofd?id=target-processes for which
-  // processes are eligible.
-  //
-  // On unmodified userdebug builds, this will lead to system crashes. Zygote
-  // will crash when trying to launch a new process as it will have an
-  // unexpected open socket to heapprofd.
-  //
-  // heapprofd will likely be overloaded by the amount of data for low
-  // sampling intervals.
-  optional bool all = 5;
-
-  // Do not emit function names for mappings starting with this prefix.
-  // E.g. /system to not emit symbols for any system libraries.
-  repeated string skip_symbol_prefix = 7;
-
-  // Dump at a predefined interval.
-  optional ContinuousDumpConfig continuous_dump_config = 6;
-
-  // Size of the shared memory buffer between the profiled processes and
-  // heapprofd. Defaults to 8 MiB. If larger than 500 MiB, truncated to 500
-  // MiB.
-  //
-  // Needs to be:
-  // * at least 8192,
-  // * a power of two,
-  // * a multiple of 4096.
-  optional uint64 shmem_size_bytes = 8;
-
-  // When the shmem buffer is full, block the client instead of ending the
-  // trace. Use with caution as this will significantly slow down the target
-  // process.
-  optional bool block_client = 9;
-
-  // If set, stop the trace session after blocking the client for this
-  // timeout. Needs to be larger than 100 us, otherwise no retries are done.
-  optional uint32 block_client_timeout_us = 14;
-
-  // Do not profile processes from startup, only match already running
-  // processes.
-  //
-  // Can not be set at the same time as no_running.
-  optional bool no_startup = 10;
-
-  // Do not profile running processes. Only match processes on startup.
-  //
-  // Can not be set at the same time as no_startup.
-  optional bool no_running = 11;
-
-  // Gather information on how many bytes of allocations are on non-referenced
-  // pages. The way to use this generally is:
-  // 1. Start profile of app.
-  // 2. Start app.
-  // 3. Trigger a dump by sending SIGUSR1 to heapprofd.
-  // 4. Do operations.
-  // 5. End profile.
-  //
-  // You can then find the allocations that were not used for the operations you
-  // did in step 4.
-  optional bool idle_allocations = 12;
-
-  // Cause heapprofd to emit a single dump at the end, showing the memory usage
-  // at the point in time when the sampled heap usage of the process was at its
-  // maximum. This causes ProfilePacket.HeapSample.self_max to be set, and
-  // self_allocated and self_freed to not be set.
-  optional bool dump_at_max = 13;
-}
-
-// End of protos/perfetto/config/profiling/heapprofd_config.proto
-
-// Begin of protos/perfetto/config/profiling/java_hprof_config.proto
-
-// Configuration for go/heapprofd.
-message JavaHprofConfig {
-  // If dump_interval_ms != 0, the following configuration is used.
-  message ContinuousDumpConfig {
-    // ms to wait before first continuous dump.
-    // A dump is always created at the beginning of the trace.
-    optional uint32 dump_phase_ms = 1;
-    // ms to wait between following dumps.
-    optional uint32 dump_interval_ms = 2;
-  }
-
-  // This input is normalized in the following way: if it contains slashes,
-  // everything up to the last slash is discarded. If it contains "@",
-  // everything after the first @ is discared.
-  // E.g. /system/bin/surfaceflinger@1.0 normalizes to surfaceflinger.
-  // This transformation is also applied to the processes' command lines when
-  // matching.
-  repeated string process_cmdline = 1;
-
-  // For watermark based triggering or local debugging.
-  repeated uint64 pid = 2;
-
-  // Dump at a predefined interval.
-  optional ContinuousDumpConfig continuous_dump_config = 3;
-}
-
-// End of protos/perfetto/config/profiling/java_hprof_config.proto
-
-// Begin of protos/perfetto/config/profiling/perf_event_config.proto
-
-// TODO(b/144281346): unstable, do not use. Will change in incompatible ways.
-message PerfEventConfig {
-  optional int32 tid = 1;
-}
-
-// End of protos/perfetto/config/profiling/perf_event_config.proto
-
-// Begin of protos/perfetto/config/gpu/gpu_counter_config.proto
-
-message GpuCounterConfig {
-  // Desired sampling interval for counters.
-  optional uint64 counter_period_ns = 1;
-
-  // List of counters to be sampled. Counter IDs correspond to the ones
-  // described in GpuCounterSpec in the data source descriptor.
-  repeated uint32 counter_ids = 2;
-
-  // Sample counters by instrumenting command buffers.
-  optional bool instrumented_sampling = 3;
-
-  // Fix gpu clock rate during trace session.
-  optional bool fix_gpu_clock = 4;
-}
-
-// End of protos/perfetto/config/gpu/gpu_counter_config.proto
-
-// Begin of protos/perfetto/config/gpu/vulkan_memory_config.proto
-
-message VulkanMemoryConfig {
-  // Tracking driver memory usage events
-  optional bool track_driver_memory_usage = 1;
-
-  // Tracking device memory usage events
-  optional bool track_device_memory_usage = 2;
-}
-
-// End of protos/perfetto/config/gpu/vulkan_memory_config.proto
-
-// Begin of protos/perfetto/config/android/packages_list_config.proto
-
-// Data source that lists details (such as version code) about packages on an
-// Android device.
-message PackagesListConfig {
-  // If not empty, emit info about only the following list of package names
-  // (exact match, no regex). Otherwise, emit info about all packages.
-  repeated string package_name_filter = 1;
-}
-
-// End of protos/perfetto/config/android/packages_list_config.proto
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/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 fc406c9..cc7e4d4 100644
--- a/protos/perfetto/config/process_stats/process_stats_config.proto
+++ b/protos/perfetto/config/process_stats/process_stats_config.proto
@@ -56,4 +56,11 @@
   // |proc_stats_poll_ms|. Non-multiples will be rounded down to the nearest
   // multiple.
   optional uint32 proc_stats_cache_ttl_ms = 6;
+
+  // Whether to record /proc/tid/time_in_state.
+  optional bool record_thread_time_in_state = 7;
+
+  // Size of the cache for thread time_in_state cpu freq values.
+  // If not specificed, the default is used.
+  optional uint32 thread_time_in_state_cache_size = 8;
 }
diff --git a/protos/perfetto/config/profiling/heapprofd_config.proto b/protos/perfetto/config/profiling/heapprofd_config.proto
index 15a7c68..10ae3b7 100644
--- a/protos/perfetto/config/profiling/heapprofd_config.proto
+++ b/protos/perfetto/config/profiling/heapprofd_config.proto
@@ -19,6 +19,7 @@
 package perfetto.protos;
 
 // Configuration for go/heapprofd.
+// Next id: 19
 message HeapprofdConfig {
   message ContinuousDumpConfig {
     // ms to wait before first dump.
@@ -58,6 +59,16 @@
   // sampling intervals.
   optional bool all = 5;
 
+  // Do not profile processes whose anon RSS + swap < given value.
+  optional uint32 min_anonymous_memory_kb = 15;
+
+  // Stop profile if heapprofd memory usage goes beyond the given value.
+  optional uint32 max_heapprofd_memory_kb = 16;
+
+  // Stop profile if heapprofd CPU time since start of this data-source
+  // goes beyond given value.
+  optional uint64 max_heapprofd_cpu_secs = 17;
+
   // Do not emit function names for mappings starting with this prefix.
   // E.g. /system to not emit symbols for any system libraries.
   repeated string skip_symbol_prefix = 7;
@@ -112,4 +123,11 @@
   // maximum. This causes ProfilePacket.HeapSample.self_max to be set, and
   // self_allocated and self_freed to not be set.
   optional bool dump_at_max = 13;
+
+  // FEATURE FLAGS. THERE BE DRAGONS.
+
+  // Escape hatch if the session is being torn down because of a forked child
+  // that shares memory space, but is not correctly identified as a vforked
+  // child.
+  optional bool disable_fork_teardown = 18;
 }
diff --git a/protos/perfetto/config/profiling/java_hprof_config.proto b/protos/perfetto/config/profiling/java_hprof_config.proto
index 572df33..7a6c652 100644
--- a/protos/perfetto/config/profiling/java_hprof_config.proto
+++ b/protos/perfetto/config/profiling/java_hprof_config.proto
@@ -42,4 +42,15 @@
 
   // Dump at a predefined interval.
   optional ContinuousDumpConfig continuous_dump_config = 3;
+
+  // Do not profile processes whose anon RSS + swap < given value.
+  optional uint32 min_anonymous_memory_kb = 4;
+
+  // Include the process' /proc/self/smaps.
+  // This only shows maps that:
+  // * start with /system
+  // * start with /vendor
+  // * start with /data/app
+  // * contain "extracted in memory from Y", where Y matches any of the above
+  optional bool dump_smaps = 5;
 }
diff --git a/protos/perfetto/config/profiling/perf_event_config.proto b/protos/perfetto/config/profiling/perf_event_config.proto
index 0333d71..f5b186c 100644
--- a/protos/perfetto/config/profiling/perf_event_config.proto
+++ b/protos/perfetto/config/profiling/perf_event_config.proto
@@ -18,7 +18,63 @@
 
 package perfetto.protos;
 
-// TODO(b/144281346): unstable, do not use. Will change in incompatible ways.
+// Configuration for the traced_perf profiler.
+//
+// At the time of writing, the config options are restricted to the periodic
+// system-wide stack sampling use-case (|all_cpus| must be true).
 message PerfEventConfig {
-  optional int32 tid = 1;
+  // If true, sample events on all CPUs.
+  optional bool all_cpus = 1;
+
+  // Per-cpu sampling frequency (requested from the kernel). Not guaranteed to
+  // be honored as the kernel can throttle the sampling rate if it's too high.
+  // If unset, an implementation-defined default is used.
+  optional uint32 sampling_frequency = 2;
+
+  // How often the per-cpu ring buffers are read by the producer.
+  // If unset, an implementation-defined default is used.
+  optional uint32 ring_buffer_read_period_ms = 8;
+
+  // Size (in 4k pages) of each per-cpu ring buffer that is filled by the
+  // kernel. If set, must be a power of two.
+  // If unset, an implementation-defined default is used.
+  optional uint32 ring_buffer_pages = 3;
+
+  // Process ID (TGID) whitelist. If this list is not empty, only matching
+  // samples will be retained. If multiple whitelists and blacklists are
+  // specified by the config, then all of them are evaluated for each sampled
+  // process.
+  repeated int32 target_pid = 4;
+
+  // Command line whitelist, matched against the
+  // /proc/<pid>/cmdline (not the comm string), with both sides being
+  // "normalized". Normalization is as follows: (1) trim everything beyond the
+  // first null or "@" byte; (2) if the string contains forward slashes, trim
+  // everything up to and including the last one.
+  repeated string target_cmdline = 5;
+
+  // PID blacklist.
+  repeated int32 exclude_pid = 6;
+
+  // Command line blacklist. Normalized in the same way as |target_cmdline|.
+  repeated string exclude_cmdline = 7;
+
+  ////////////////////
+  // Uncommon options:
+
+  // Timeout for the remote /proc/<pid>/{maps,mem} file descriptors for a
+  // sampled process. This is primarily for Android, where this lookup is
+  // asynchronous. As long as the producer is waiting, the associated samples
+  // will be kept enqueued (putting pressure on the capacity of the shared
+  // unwinding queue). Once a lookup for a process expires, all associated
+  // samples are discarded. However, if the lookup still succeeds after the
+  // timeout, future samples will be handled normally.
+  // If unset, an implementation-defined default is used.
+  optional uint32 remote_descriptor_timeout_ms = 9;
+
+  // Optional period for clearing state cached by the unwinder. This is a heavy
+  // operation that is only necessary for traces that target a wide set of
+  // processes, and require the memory footprint to be reset periodically.
+  // If unset, the cached state will not be cleared.
+  optional uint32 unwind_state_clear_period_ms = 10;
 }
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/trace_config.proto b/protos/perfetto/config/trace_config.proto
index 085f10c..852f18c 100644
--- a/protos/perfetto/config/trace_config.proto
+++ b/protos/perfetto/config/trace_config.proto
@@ -25,7 +25,7 @@
 // It contains the general config for the logging buffer(s) and the configs for
 // all the data source being enabled.
 //
-// Next id: 29.
+// Next id: 30.
 message TraceConfig {
   message BufferConfig {
     optional uint32 size_kb = 1;
@@ -58,30 +58,40 @@
     optional protos.DataSourceConfig config = 1;
 
     // Optional. If multiple producers (~processes) expose the same data source
-    // and |producer_name_filter| != "", the data source is enabled only for
-    // producers whose names match any of the producer_name_filter below.
-    // The |producer_name_filter| has to be an exact match. (TODO(primiano):
-    // support wildcards or regex).
+    // and either |producer_name_filter| or |producer_name_regex_filter| is set,
+    // the data source is enabled only for producers whose names match any of
+    // the filters.
+    // |producer_name_filter| has to be an exact match, while
+    // |producer_name_regex_filter| is a regular expression.
     // This allows to enable a data source only for specific processes.
-    // The "repeated" field has OR sematics: specifying a filter ["foo", "bar"]
-    // will enable data source on both "foo" and "bar" (if existent).
+    // The "repeated" fields have OR semantics: specifying a filter ["foo",
+    // "bar"] will enable data sources on both "foo" and "bar" (if they exist).
     repeated string producer_name_filter = 2;
+    repeated string producer_name_regex_filter = 3;
   }
   repeated DataSource data_sources = 2;
 
-  // Config for builtin trace packets emitted by perfetto like trace stats,
-  // system info, etc.
+  // Config for disabling builtin data sources in the tracing service.
   message BuiltinDataSource {
     // Disable emitting clock timestamps into the trace.
     optional bool disable_clock_snapshotting = 1;
 
+    // Disable echoing the original trace config in the trace.
     optional bool disable_trace_config = 2;
 
+    // Disable emitting system info (build fingerprint, cpuinfo, etc).
     optional bool disable_system_info = 3;
+
+    // Disable emitting events for data-source state changes (e.g. the marker
+    // for all data sources having ACKed the start of the trace).
+    optional bool disable_service_events = 4;
   }
   optional BuiltinDataSource builtin_data_sources = 20;
 
   // If specified, the trace will be stopped |duration_ms| after starting.
+  // This does *not* count the time the system is suspended, so we will run
+  // for duration_ms of system activity, not wall time.
+  //
   // However in case of traces with triggers, see
   // TriggerConfig.trigger_timeout_ms instead.
   optional uint32 duration_ms = 3;
@@ -133,11 +143,21 @@
   // Statsd-specific metadata.
   optional StatsdMetadata statsd_metadata = 7;
 
-  // When true, the EnableTracing() request must also provide a file descriptor.
-  // The service will then periodically read packets out of the trace buffer and
-  // store it into the passed file.
+  // When true && |output_path| is empty, the EnableTracing() request must
+  // provide a file descriptor. The service will then periodically read packets
+  // out of the trace buffer and store it into the passed file.
+  // If |output_path| is not empty no fd should be passed, the service
+  // will create a new file and write into that (see comment below).
   optional bool write_into_file = 8;
 
+  // This must point to a non-existing file. If the file exists the service
+  // will NOT overwrite and will fail instead as a security precaution.
+  // On Android, when this is used with the system traced, the path must be
+  // within /data/misc/perfetto-traces/ or the trace will fail.
+  // This option has been introduced in Android R. Before R write_into_file
+  // can be used only with the "pass a file descriptor over IPC" mode.
+  optional string output_path = 29;
+
   // Optional. If non-zero tunes the write period. A min value of 100ms is
   // enforced (i.e. smaller values are ignored).
   optional uint32 file_write_period_ms = 9;
diff --git a/protos/perfetto/config/track_event/BUILD.gn b/protos/perfetto/config/track_event/BUILD.gn
new file mode 100644
index 0000000..8f16db8
--- /dev/null
+++ b/protos/perfetto/config/track_event/BUILD.gn
@@ -0,0 +1,19 @@
+# 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/proto_library.gni")
+
+perfetto_proto_library("@TYPE@") {
+  sources = [ "track_event_config.proto" ]
+}
diff --git a/protos/perfetto/config/track_event/track_event_config.proto b/protos/perfetto/config/track_event/track_event_config.proto
new file mode 100644
index 0000000..6ae1f8f
--- /dev/null
+++ b/protos/perfetto/config/track_event/track_event_config.proto
@@ -0,0 +1,59 @@
+/*
+ * 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;
+
+message TrackEventConfig {
+  // The following fields define the set of enabled trace categories. Each list
+  // item is a glob.
+  //
+  // To determine if category is enabled, it is checked against the filters in
+  // the following order:
+  //
+  //   1. Exact matches in enabled categories.
+  //   2. Exact matches in enabled tags.
+  //   3. Exact matches in disabled categories.
+  //   4. Exact matches in disabled tags.
+  //   5. Pattern matches in enabled categories.
+  //   6. Pattern matches in enabled tags.
+  //   7. Pattern matches in disabled categories.
+  //   8. Pattern matches in disabled tags.
+  //
+  // If none of the steps produced a match, the category is enabled by default.
+  //
+  // Examples:
+  //
+  //  - To enable all non-slow/debug categories:
+  //
+  //       No configuration needed, happens by default.
+  //
+  //  - To enable a specific category:
+  //
+  //       disabled_categories = ["*"]
+  //       enabled_categories = ["my_category"]
+  //
+  //  - To enable only categories with a specific tag:
+  //
+  //       disabled_tags = ["*"]
+  //       enabled_tags = ["my_tag"]
+  //
+  repeated string disabled_categories = 1;  // Default: []
+  repeated string enabled_categories = 2;   // Default: []
+  repeated string disabled_tags = 3;  // Default: [“slow”, “debug”]
+  repeated string enabled_tags = 4;   // Default: []
+}
diff --git a/protos/perfetto/ipc/BUILD.gn b/protos/perfetto/ipc/BUILD.gn
index 383ae5f..3388b7dd 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 6a6e3ca..b8b929c 100644
--- a/protos/perfetto/ipc/consumer_port.proto
+++ b/protos/perfetto/ipc/consumer_port.proto
@@ -18,6 +18,7 @@
 
 import "protos/perfetto/common/observable_events.proto";
 import "protos/perfetto/common/tracing_service_state.proto";
+import "protos/perfetto/common/tracing_service_capabilities.proto";
 import "protos/perfetto/common/trace_stats.proto";
 import "protos/perfetto/config/trace_config.proto";
 
@@ -106,6 +107,11 @@
   // Allows to obtain the list of data sources connected and their descriptors.
   rpc QueryServiceState(QueryServiceStateRequest)
       returns (stream QueryServiceStateResponse) {}
+
+  // Obtains a list of features supported by the service. This is to deal with
+  // backward/forward compatibility and feature detection.
+  rpc QueryCapabilities(QueryCapabilitiesRequest)
+      returns (QueryCapabilitiesResponse) {}
 }
 
 // Arguments for rpc EnableTracing().
@@ -220,9 +226,20 @@
   optional ObservableEvents events = 1;
 }
 
-// Arguments for rpc QueryServiceState
+// Arguments for rpc QueryServiceState.
 message QueryServiceStateRequest {}
 
 message QueryServiceStateResponse {
+  // In order to avoid hitting IPC message size limitations, the service will
+  // return >1 replies for each query, chunking the TracingServiceState. The
+  // receiver is expected to merge replies together and parse that when the
+  // last reply is received (i.e. when IPC's |has_more| == false).
   optional TracingServiceState service_state = 1;
 }
+
+// Arguments for rpc QueryCapabilities.
+message QueryCapabilitiesRequest {}
+
+message QueryCapabilitiesResponse {
+  optional TracingServiceCapabilities capabilities = 1;
+}
\ No newline at end of file
diff --git a/protos/perfetto/ipc/producer_port.proto b/protos/perfetto/ipc/producer_port.proto
index fc09783..76eb0ef 100644
--- a/protos/perfetto/ipc/producer_port.proto
+++ b/protos/perfetto/ipc/producer_port.proto
@@ -78,6 +78,20 @@
   // 2) Perform an action as defined in those sessions configs.
   rpc ActivateTriggers(ActivateTriggersRequest)
       returns (ActivateTriggersResponse) {}
+
+  // ----------------------------------------------------
+  // All methods below have been introduced in Android R.
+  // ----------------------------------------------------
+
+  // This is used to linearize the producer with the service and to guarantee
+  // that all IPCs prior to this call have been seen and processed by the
+  // service. Example:
+  //   - RegisterDataSource(A)
+  //   - RegisterDataSource(B)
+  //   - Sync()
+  // When the Sync() response is received, the producer is guaranteed that the
+  // service has seen both data source registrations.
+  rpc Sync(SyncRequest) returns (SyncResponse) {}
 }
 
 // Arguments for rpc InitializeConnection().
@@ -124,10 +138,27 @@
   // error reporting, to print a log message when a producer connects to a
   // service that has mismatching build flags.
   optional ProducerBuildFlags build_flags = 5;
+
+  // ---------------------------------------------------
+  // All fields below have been introduced in Android R.
+  // ---------------------------------------------------
+
+  // Since Android R, this request can also transport an FD for the producer's
+  // shared memory buffer, if allocated by the producer (e.g. for startup
+  // tracing). In this case, |shared_memory_page_size_hint_bytes| is a required
+  // field, and describes the SMB's page size. Note that the service may not
+  // accept this SMB (e.g. because it is too old or its size / page size are
+  // invalid) and instead allocate a new SMB which is provided in the
+  // SetupTracing response. See TracingService::ConnectProducer() and
+  // |using_shmem_provided_by_producer| in InitializeConnectionResponse.
+  optional bool producer_provided_shmem = 6;
 }
 
 message InitializeConnectionResponse {
-  // This message provides the shared memory buffer FD (not a proto field).
+  // Indicates whether the service accepted the SMB provided by the producer in
+  // InitializeConnectionRequest (if any). If false, the shared memory buffer FD
+  // will provided by the service via the SetupTracing async command.
+  optional bool using_shmem_provided_by_producer = 1;
 }
 
 // Arguments for rpc RegisterDataSource().
@@ -227,7 +258,7 @@
   message StopDataSource { optional uint64 instance_id = 1; }
 
   // This message also transports the file descriptor for the shared memory
-  // buffer.
+  // buffer (not a proto field).
   message SetupTracing { optional uint32 shared_buffer_page_size_kb = 1; }
 
   message Flush {
@@ -263,3 +294,7 @@
     ClearIncrementalState clear_incremental_state = 7;
   }
 }
+
+// Arguments for rpc Sync().
+message SyncRequest {}
+message SyncResponse {}
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 9b51e5b..6a15c3d 100644
--- a/protos/perfetto/metrics/android/BUILD.gn
+++ b/protos/perfetto/metrics/android/BUILD.gn
@@ -18,9 +18,11 @@
   sources = [
     "batt_metric.proto",
     "cpu_metric.proto",
+    "display_metrics.proto",
     "heap_profile_callsites.proto",
     "hwui_metric.proto",
     "ion_metric.proto",
+    "java_heap_histogram.proto",
     "java_heap_stats.proto",
     "lmk_metric.proto",
     "lmk_reason_metric.proto",
@@ -30,6 +32,8 @@
     "powrails_metric.proto",
     "process_metadata.proto",
     "startup_metric.proto",
+    "task_names.proto",
+    "thread_time_in_state_metric.proto",
     "unmapped_java_symbols.proto",
     "unsymbolized_frames.proto",
   ]
diff --git a/protos/perfetto/metrics/android/batt_metric.proto b/protos/perfetto/metrics/android/batt_metric.proto
index d9d7876..8f28146 100644
--- a/protos/perfetto/metrics/android/batt_metric.proto
+++ b/protos/perfetto/metrics/android/batt_metric.proto
@@ -29,7 +29,16 @@
     optional double current_avg_ua = 5;
   }
 
+  message BatteryAggregates {
+    // Field numbers for these 3 == the int values fromm Android
+    optional int64 total_screen_off_ns = 1;
+    optional int64 total_screen_on_ns = 2;
+    optional int64 total_screen_doze_ns = 3;
+  }
+
   // Battery counters info for each ts of the trace. This should only be
   // extracted for short traces.
   repeated BatteryCounters battery_counters = 1;
+
+  optional BatteryAggregates battery_aggregates = 2;
 }
diff --git a/protos/perfetto/metrics/android/cpu_metric.proto b/protos/perfetto/metrics/android/cpu_metric.proto
index 12f7588..7c3b036 100644
--- a/protos/perfetto/metrics/android/cpu_metric.proto
+++ b/protos/perfetto/metrics/android/cpu_metric.proto
@@ -61,13 +61,15 @@
     reserved 3;
   }
 
-  // Next id: 4
+  // Next id: 8
   message Process {
     optional string name = 1;
     optional Metrics metrics = 4;
 
     // Breakdowns of above metrics.
-    repeated Thread threads = 2;
+    repeated Thread threads = 6;
+    repeated CoreData core = 7;
+    repeated CoreTypeData core_type = 5;
 
     reserved 3;
   }
diff --git a/protos/perfetto/metrics/android/display_metrics.proto b/protos/perfetto/metrics/android/display_metrics.proto
new file mode 100644
index 0000000..fc858ac
--- /dev/null
+++ b/protos/perfetto/metrics/android/display_metrics.proto
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+message AndroidDisplayMetrics {
+  // Stat that reports the number of duplicate frames submitted
+  // to the display for rendering. That is frames that have the same
+  // pixels values but where still submitted. It is tracked based on
+  // comparing the MISR of the current frame vs previous frame.
+  optional uint32 total_duplicate_frames = 1;
+
+  // Stat reports whether there is any duplicate_frames tracked
+  optional uint32 duplicate_frames_logged = 2;
+}
\ No newline at end of file
diff --git a/protos/perfetto/metrics/android/ion_metric.proto b/protos/perfetto/metrics/android/ion_metric.proto
index c5a5b19..810529c 100644
--- a/protos/perfetto/metrics/android/ion_metric.proto
+++ b/protos/perfetto/metrics/android/ion_metric.proto
@@ -25,6 +25,10 @@
     optional double avg_size_bytes = 2;
     optional double min_size_bytes = 3;
     optional double max_size_bytes = 4;
+
+    // Total allocation size.
+    // Essentially the sum of positive allocs (-> new buffers).
+    optional double total_alloc_size_bytes = 5;
   }
 
   repeated Buffer buffer = 1;
diff --git a/protos/perfetto/metrics/android/java_heap_histogram.proto b/protos/perfetto/metrics/android/java_heap_histogram.proto
new file mode 100644
index 0000000..f0680a0
--- /dev/null
+++ b/protos/perfetto/metrics/android/java_heap_histogram.proto
@@ -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.
+ */
+syntax = "proto2";
+package perfetto.protos;
+
+import "protos/perfetto/metrics/android/process_metadata.proto";
+
+message JavaHeapHistogram {
+  message TypeCount {
+    optional string type_name = 1;
+    optional uint32 obj_count = 2;
+    optional uint32 reachable_obj_count = 3;
+  }
+
+  message Sample {
+    optional int64 ts = 1;
+    repeated TypeCount type_count = 2;
+  }
+
+  // Heap stats per process. One sample per dump (with continuous dump you can
+  // have more samples differentiated by ts).
+  message InstanceStats {
+    optional uint32 upid = 1;
+    optional AndroidProcessMetadata process = 2;
+    repeated Sample samples = 3;
+  }
+
+  repeated InstanceStats instance_stats = 1;
+}
diff --git a/protos/perfetto/metrics/android/java_heap_stats.proto b/protos/perfetto/metrics/android/java_heap_stats.proto
index 85d0595..edf8e53 100644
--- a/protos/perfetto/metrics/android/java_heap_stats.proto
+++ b/protos/perfetto/metrics/android/java_heap_stats.proto
@@ -20,10 +20,17 @@
 import "protos/perfetto/metrics/android/process_metadata.proto";
 
 message JavaHeapStats {
+  // Next id: 7
   message Sample {
     optional int64 ts = 1;
+    // Size of the Java heap in bytes
     optional int64 heap_size = 2;
+    optional int64 obj_count = 4;
+    // Size of the reachable objects in bytes.
     optional int64 reachable_heap_size = 3;
+    optional int64 reachable_obj_count = 5;
+    // Sum of anonymous RSS + swap pages in bytes.
+    optional int64 anon_rss_and_swap_size = 6;
   }
 
   // Heap stats per process. One sample per dump (can be > 1 if continuous
diff --git a/protos/perfetto/metrics/android/lmk_metric.proto b/protos/perfetto/metrics/android/lmk_metric.proto
index be4218f..83fc090 100644
--- a/protos/perfetto/metrics/android/lmk_metric.proto
+++ b/protos/perfetto/metrics/android/lmk_metric.proto
@@ -28,4 +28,8 @@
   // Total count of LMK events observed in the trace.
   optional int32 total_count = 1;
   repeated ByOomScore by_oom_score = 2;
+
+  // OOM reaper kills. Enabled via the oom/mark_victim point. Should never
+  // happen.
+  optional int32 oom_victim_count = 3;
 }
diff --git a/protos/perfetto/metrics/android/startup_metric.proto b/protos/perfetto/metrics/android/startup_metric.proto
index d38a598..ce7e9e3 100644
--- a/protos/perfetto/metrics/android/startup_metric.proto
+++ b/protos/perfetto/metrics/android/startup_metric.proto
@@ -18,6 +18,8 @@
 
 package perfetto.protos;
 
+import "protos/perfetto/metrics/android/process_metadata.proto";
+
 // Android app startup metrics.
 message AndroidStartupMetric {
   // A simplified view of the task state durations for a thread
@@ -29,13 +31,17 @@
     optional int64 interruptible_sleep_dur_ns = 4;
   }
 
-  message Slice { optional int64 dur_ns = 1; }
+  message Slice {
+    optional int64 dur_ns = 1;
+    optional double dur_ms = 2;
+  }
 
   // Timing information spanning the intent received by the
   // activity manager to the first frame drawn.
-  // All times and durations in nanoseconds (ns).
+  // Next id: 21.
   message ToFirstFrame {
     optional int64 dur_ns = 1;
+    optional double dur_ms = 17;
     optional TaskStateBreakdown main_thread_by_task_state = 2;
 
     // In this timespan, how many processes (apart from the main activity) were
@@ -60,12 +66,28 @@
     // The actual duration of the process start (based on the zygote slice).
     optional Slice time_during_start_process = 11;
 
-    // The ratio between the cpu time of the activity process
-    // to all other processes in the system.
-    optional double other_process_to_activity_cpu_ratio = 12;
+    optional Slice to_post_fork = 18;
+    optional Slice to_activity_thread_main = 19;
+    optional Slice to_bind_application = 20;
+
+    optional Slice time_post_fork = 16;
+
+    // Deprecated was other_process_to_activity_cpu_ratio
+    reserved 12;
+
+    // Removed: was uint32 versions of to_post_fork, to_activity_thread_main and
+    // to_bind_application.
+    reserved 13, 14, 15;
   }
 
-  // Next id: 7
+  // Metrics about startup which were developed by looking at experiments using
+  // high-speed cameras (HSC).
+  message HscMetrics {
+    // The duration of the full "startup" as defined by HSC tests.
+    optional Slice full_startup = 1;
+  }
+
+  // Next id: 8
   message Startup {
     // Random id uniquely identifying an app startup in this trace.
     optional uint32 startup_id = 1;
@@ -86,6 +108,11 @@
     optional uint32 activity_hosting_process_count = 6;
 
     optional ToFirstFrame to_first_frame = 5;
+
+    // Details about the process (uid, version, etc)
+    optional AndroidProcessMetadata process = 7;
+
+    optional HscMetrics hsc = 8;
   }
 
   repeated Startup startup = 1;
diff --git a/protos/perfetto/metrics/android/task_names.proto b/protos/perfetto/metrics/android/task_names.proto
new file mode 100644
index 0000000..3aed87f
--- /dev/null
+++ b/protos/perfetto/metrics/android/task_names.proto
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+syntax = "proto2";
+package perfetto.protos;
+
+message AndroidTaskNames {
+  message Process {
+    optional int64 pid = 1;
+
+    // Process name.
+    optional string process_name = 2;
+
+    // Names of all threads for this process.
+    repeated string thread_name = 3;
+
+    // User id under which this process runs.
+    optional int64 uid = 4;
+
+    // Packages matching the process uid.
+    repeated string uid_package_name = 5;
+  }
+
+  repeated Process process = 1;
+}
diff --git a/protos/perfetto/metrics/android/thread_time_in_state_metric.proto b/protos/perfetto/metrics/android/thread_time_in_state_metric.proto
new file mode 100644
index 0000000..1289b46
--- /dev/null
+++ b/protos/perfetto/metrics/android/thread_time_in_state_metric.proto
@@ -0,0 +1,41 @@
+/*
+ * 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;
+
+import "protos/perfetto/metrics/android/process_metadata.proto";
+
+message AndroidThreadTimeInStateMetric {
+  message MetricsByCoreType {
+    optional string core_type = 1;
+    optional int64 runtime_ms = 2;
+  }
+
+  message Thread {
+    optional string name = 1;
+    repeated MetricsByCoreType metrics_by_core_type = 2;
+  }
+
+  message Process {
+    optional AndroidProcessMetadata metadata = 1;
+    repeated MetricsByCoreType metrics_by_core_type = 2;
+    repeated Thread threads = 3;
+  }
+
+  repeated Process processes = 1;
+}
diff --git a/protos/perfetto/metrics/android/unmapped_java_symbols.proto b/protos/perfetto/metrics/android/unmapped_java_symbols.proto
index b8c8834..b14cb5d 100644
--- a/protos/perfetto/metrics/android/unmapped_java_symbols.proto
+++ b/protos/perfetto/metrics/android/unmapped_java_symbols.proto
@@ -21,10 +21,17 @@
 import "protos/perfetto/metrics/android/process_metadata.proto";
 
 message UnmappedJavaSymbols {
+  message Field {
+    optional string field_name = 1;
+    optional string field_type_name = 2;
+  }
+
   message ProcessSymbols {
+    reserved 3;
+
     optional AndroidProcessMetadata process_metadata = 1;
     repeated string type_name = 2;
-    repeated string field_name = 3;
+    repeated Field field = 4;
   }
 
   repeated ProcessSymbols process_symbols = 1;
diff --git a/protos/perfetto/metrics/metrics.proto b/protos/perfetto/metrics/metrics.proto
index 16be108..e36e0f1 100644
--- a/protos/perfetto/metrics/metrics.proto
+++ b/protos/perfetto/metrics/metrics.proto
@@ -32,7 +32,11 @@
 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";
+import "protos/perfetto/metrics/android/java_heap_histogram.proto";
 import "protos/perfetto/metrics/android/java_heap_stats.proto";
+import "protos/perfetto/metrics/android/display_metrics.proto";
+import "protos/perfetto/metrics/android/task_names.proto";
+import "protos/perfetto/metrics/android/thread_time_in_state_metric.proto";
 
 // Trace processor metadata
 message TraceMetadata {
@@ -48,11 +52,12 @@
   optional string android_build_fingerprint = 4;
   optional int64 statsd_triggering_subscription_id = 5;
   optional int64 trace_size_bytes = 6;
+  repeated string trace_trigger = 7;
 }
 
 // Root message for all Perfetto-based metrics.
 //
-// Next id: 21
+// Next id: 25
 message TraceMetrics {
   reserved 4, 10, 13, 14;
 
@@ -98,6 +103,9 @@
   // If the trace contains a heap graph, output allocation statistics.
   optional JavaHeapStats java_heap_stats = 17;
 
+  // If the trace contains a heap graph, output histogram.
+  optional JavaHeapHistogram java_heap_histogram = 21;
+
   // Metrics used to find potential culprits of low-memory kills.
   optional AndroidLmkReasonMetric android_lmk_reason = 18;
 
@@ -106,6 +114,12 @@
 
   optional AndroidHwuiMetric android_hwui_metric = 20;
 
+  optional AndroidDisplayMetrics display_metrics = 22;
+
+  optional AndroidTaskNames android_task_names = 23;
+
+  optional AndroidThreadTimeInStateMetric android_thread_time_in_state = 24;
+
   // 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 4cf3399..f92b305 100644
--- a/protos/perfetto/metrics/perfetto_merged_metrics.proto
+++ b/protos/perfetto/metrics/perfetto_merged_metrics.proto
@@ -24,9 +24,18 @@
     optional double current_avg_ua = 5;
   }
 
+  message BatteryAggregates {
+    // Field numbers for these 3 == the int values fromm Android
+    optional int64 total_screen_off_ns = 1;
+    optional int64 total_screen_on_ns = 2;
+    optional int64 total_screen_doze_ns = 3;
+  }
+
   // Battery counters info for each ts of the trace. This should only be
   // extracted for short traces.
   repeated BatteryCounters battery_counters = 1;
+
+  optional BatteryAggregates battery_aggregates = 2;
 }
 
 // End of protos/perfetto/metrics/android/batt_metric.proto
@@ -76,13 +85,15 @@
     reserved 3;
   }
 
-  // Next id: 4
+  // Next id: 8
   message Process {
     optional string name = 1;
     optional Metrics metrics = 4;
 
     // Breakdowns of above metrics.
-    repeated Thread threads = 2;
+    repeated Thread threads = 6;
+    repeated CoreData core = 7;
+    repeated CoreTypeData core_type = 5;
 
     reserved 3;
   }
@@ -92,102 +103,19 @@
 
 // End of protos/perfetto/metrics/android/cpu_metric.proto
 
-// Begin of protos/perfetto/metrics/android/mem_metric.proto
+// Begin of protos/perfetto/metrics/android/display_metrics.proto
 
-// Memory metrics on Android.
-message AndroidMemoryMetric {
-  message ProcessMetrics {
-    optional string process_name = 1;
-    optional ProcessMemoryCounters total_counters = 2;
-    repeated PriorityBreakdown priority_breakdown = 3;
-  }
+message AndroidDisplayMetrics {
+  // Stat that reports the number of duplicate frames submitted
+  // to the display for rendering. That is frames that have the same
+  // pixels values but where still submitted. It is tracked based on
+  // comparing the MISR of the current frame vs previous frame.
+  optional uint32 total_duplicate_frames = 1;
 
-  message PriorityBreakdown {
-    optional string priority = 1;
-    optional ProcessMemoryCounters counters = 2;
-  }
-
-  message ProcessMemoryCounters {
-    optional Counter anon_rss = 1;
-    optional Counter file_rss = 2;
-    optional Counter swap = 3;
-    optional Counter anon_and_swap = 4;
-
-    // Available when ART trace events are available.
-    optional Counter java_heap = 5;
-  }
-
-  message Counter {
-    optional double min = 1;
-    optional double max = 2;
-    optional double avg = 3;
-  }
-
-  // Process metrics, grouped by process name
-  repeated ProcessMetrics process_metrics = 1;
+  // Stat reports whether there is any duplicate_frames tracked
+  optional uint32 duplicate_frames_logged = 2;
 }
-
-// End of protos/perfetto/metrics/android/mem_metric.proto
-
-// Begin of protos/perfetto/metrics/android/mem_unagg_metric.proto
-
-// Unaggregated memory metrics on Android.
-message AndroidMemoryUnaggregatedMetric {
-  message ProcessValues {
-    optional string process_name = 1;
-    optional ProcessMemoryValues mem_values = 2;
-  }
-
-  message ProcessMemoryValues {
-    repeated Value anon_rss = 1;
-    repeated Value file_rss = 2;
-    repeated Value swap = 3;
-    repeated Value anon_and_swap = 4;
-  }
-
-  message Value {
-    optional int64 ts = 1;
-    optional int32 oom_score = 2;
-    optional double value = 3;
-  }
-
-  // Process metrics for every process instance in trace.
-  repeated ProcessValues process_values = 1;
-}
-
-// End of protos/perfetto/metrics/android/mem_unagg_metric.proto
-
-// Begin of protos/perfetto/metrics/android/ion_metric.proto
-
-// ion memory stats on Android.
-message AndroidIonMetric {
-  message Buffer {
-    optional string name = 1;
-    optional double avg_size_bytes = 2;
-    optional double min_size_bytes = 3;
-    optional double max_size_bytes = 4;
-  }
-
-  repeated Buffer buffer = 1;
-}
-
-// End of protos/perfetto/metrics/android/ion_metric.proto
-
-// Begin of protos/perfetto/metrics/android/lmk_metric.proto
-
-// LMK stats on Android.
-message AndroidLmkMetric {
-  message ByOomScore {
-    optional int32 oom_score_adj = 1;
-    optional int32 count = 2;
-  }
-
-  // Total count of LMK events observed in the trace.
-  optional int32 total_count = 1;
-  repeated ByOomScore by_oom_score = 2;
-}
-
-// End of protos/perfetto/metrics/android/lmk_metric.proto
+// End of protos/perfetto/metrics/android/display_metrics.proto
 
 // Begin of protos/perfetto/metrics/android/process_metadata.proto
 
@@ -223,135 +151,6 @@
 
 // End of protos/perfetto/metrics/android/process_metadata.proto
 
-// Begin of protos/perfetto/metrics/android/lmk_reason_metric.proto
-
-// Potential culplit of a low-memory kill on Android.
-message AndroidLmkReasonMetric {
-  message Process {
-    optional AndroidProcessMetadata process = 1;
-
-    // OOM score adj of the process.
-    optional int32 oom_score_adj = 2;
-
-    // RSS + swap.
-    optional int64 size = 3;
-  }
-  message Lmk {
-    // OOM score adj of the LMK'ed process.
-    optional int32 oom_score_adj = 1;
-
-    // Total size of the system ION heap in bytes during this LMK.
-    optional int64 system_ion_heap_size = 2;
-
-    // Processes present during this LMK.
-    repeated Process processes = 3;
-  }
-
-  repeated Lmk lmks = 1;
-}
-
-// End of protos/perfetto/metrics/android/lmk_reason_metric.proto
-
-// Begin of protos/perfetto/metrics/android/powrails_metric.proto
-
-message AndroidPowerRails {
-  // Energy data per Power Rail at given ts.
-  message EnergyData {
-    // Time since device boot(CLOCK_BOTTOMTIME) in milli-seconds.
-    optional int64 timestamp_ms = 1;
-    // Accumulated energy since device boot in microwatt-seconds(uws).
-    optional double energy_uws = 2;
-  }
-
-  message PowerRails {
-    // Name of the rail.
-    optional string name = 1;
-    // Energy data for given rail and for all samples in the trace.
-    repeated EnergyData energy_data = 2;
-  }
-
-  // Energy data per Power Rail.
-  repeated PowerRails power_rails = 1;
-}
-// End of protos/perfetto/metrics/android/powrails_metric.proto
-
-// Begin of protos/perfetto/metrics/android/startup_metric.proto
-
-// Android app startup metrics.
-message AndroidStartupMetric {
-  // A simplified view of the task state durations for a thread
-  // and a span of time.
-  message TaskStateBreakdown {
-    optional int64 running_dur_ns = 1;
-    optional int64 runnable_dur_ns = 2;
-    optional int64 uninterruptible_sleep_dur_ns = 3;
-    optional int64 interruptible_sleep_dur_ns = 4;
-  }
-
-  message Slice { optional int64 dur_ns = 1; }
-
-  // Timing information spanning the intent received by the
-  // activity manager to the first frame drawn.
-  // All times and durations in nanoseconds (ns).
-  message ToFirstFrame {
-    optional int64 dur_ns = 1;
-    optional TaskStateBreakdown main_thread_by_task_state = 2;
-
-    // In this timespan, how many processes (apart from the main activity) were
-    // spawned.
-    optional uint32 other_processes_spawned_count = 3;
-
-    // Total time spent in activity manager between the initial intent
-    // and the end of the activity starter.
-    optional Slice time_activity_manager = 4;
-
-    // The following slices follow the typical steps post-fork.
-    optional Slice time_activity_thread_main = 5;
-    optional Slice time_bind_application = 6;
-    optional Slice time_activity_start = 7;
-    optional Slice time_activity_resume = 8;
-    optional Slice time_choreographer = 9;
-
-    // If we are starting a new process, record the duration from the
-    // intent being received to the time we call the zygote.
-    optional Slice time_before_start_process = 10;
-
-    // The actual duration of the process start (based on the zygote slice).
-    optional Slice time_during_start_process = 11;
-
-    // The ratio between the cpu time of the activity process
-    // to all other processes in the system.
-    optional double other_process_to_activity_cpu_ratio = 12;
-  }
-
-  // Next id: 7
-  message Startup {
-    // Random id uniquely identifying an app startup in this trace.
-    optional uint32 startup_id = 1;
-
-    // Name of the package launched
-    optional string package_name = 2;
-
-    // Name of the process launched
-    optional string process_name = 3;
-
-    // Did we ask the zygote for a new process
-    optional bool zygote_new_process = 4;
-
-    // Number of processes hosting the activity involved in the launch.
-    // This will usually be 1. If it is 0, it is indicative of a data / process
-    // error. If > 1, the process died during startup and the system respawned
-    // it.
-    optional uint32 activity_hosting_process_count = 6;
-
-    optional ToFirstFrame to_first_frame = 5;
-  }
-
-  repeated Startup startup = 1;
-}
-
-// End of protos/perfetto/metrics/android/startup_metric.proto
-
 // Begin of protos/perfetto/metrics/android/heap_profile_callsites.proto
 
 message HeapProfileCallsites {
@@ -494,6 +293,196 @@
 
 // End of protos/perfetto/metrics/android/hwui_metric.proto
 
+// Begin of protos/perfetto/metrics/android/ion_metric.proto
+
+// ion memory stats on Android.
+message AndroidIonMetric {
+  message Buffer {
+    optional string name = 1;
+    optional double avg_size_bytes = 2;
+    optional double min_size_bytes = 3;
+    optional double max_size_bytes = 4;
+
+    // Total allocation size.
+    // Essentially the sum of positive allocs (-> new buffers).
+    optional double total_alloc_size_bytes = 5;
+  }
+
+  repeated Buffer buffer = 1;
+}
+
+// End of protos/perfetto/metrics/android/ion_metric.proto
+
+// Begin of protos/perfetto/metrics/android/java_heap_histogram.proto
+
+message JavaHeapHistogram {
+  message TypeCount {
+    optional string type_name = 1;
+    optional uint32 obj_count = 2;
+    optional uint32 reachable_obj_count = 3;
+  }
+
+  message Sample {
+    optional int64 ts = 1;
+    repeated TypeCount type_count = 2;
+  }
+
+  // Heap stats per process. One sample per dump (with continuous dump you can
+  // have more samples differentiated by ts).
+  message InstanceStats {
+    optional uint32 upid = 1;
+    optional AndroidProcessMetadata process = 2;
+    repeated Sample samples = 3;
+  }
+
+  repeated InstanceStats instance_stats = 1;
+}
+
+// End of protos/perfetto/metrics/android/java_heap_histogram.proto
+
+// Begin of protos/perfetto/metrics/android/java_heap_stats.proto
+
+message JavaHeapStats {
+  // Next id: 7
+  message Sample {
+    optional int64 ts = 1;
+    // Size of the Java heap in bytes
+    optional int64 heap_size = 2;
+    optional int64 obj_count = 4;
+    // Size of the reachable objects in bytes.
+    optional int64 reachable_heap_size = 3;
+    optional int64 reachable_obj_count = 5;
+    // Sum of anonymous RSS + swap pages in bytes.
+    optional int64 anon_rss_and_swap_size = 6;
+  }
+
+  // Heap stats per process. One sample per dump (can be > 1 if continuous
+  // dump is enabled).
+  message InstanceStats {
+    optional uint32 upid = 1;
+    optional AndroidProcessMetadata process = 2;
+    repeated Sample samples = 3;
+  }
+
+  repeated InstanceStats instance_stats = 1;
+}
+
+// End of protos/perfetto/metrics/android/java_heap_stats.proto
+
+// Begin of protos/perfetto/metrics/android/lmk_metric.proto
+
+// LMK stats on Android.
+message AndroidLmkMetric {
+  message ByOomScore {
+    optional int32 oom_score_adj = 1;
+    optional int32 count = 2;
+  }
+
+  // Total count of LMK events observed in the trace.
+  optional int32 total_count = 1;
+  repeated ByOomScore by_oom_score = 2;
+
+  // OOM reaper kills. Enabled via the oom/mark_victim point. Should never
+  // happen.
+  optional int32 oom_victim_count = 3;
+}
+
+// End of protos/perfetto/metrics/android/lmk_metric.proto
+
+// Begin of protos/perfetto/metrics/android/lmk_reason_metric.proto
+
+// Potential culplit of a low-memory kill on Android.
+message AndroidLmkReasonMetric {
+  message Process {
+    optional AndroidProcessMetadata process = 1;
+
+    // OOM score adj of the process.
+    optional int32 oom_score_adj = 2;
+
+    // RSS + swap.
+    optional int64 size = 3;
+  }
+  message Lmk {
+    // OOM score adj of the LMK'ed process.
+    optional int32 oom_score_adj = 1;
+
+    // Total size of the system ION heap in bytes during this LMK.
+    optional int64 system_ion_heap_size = 2;
+
+    // Processes present during this LMK.
+    repeated Process processes = 3;
+  }
+
+  repeated Lmk lmks = 1;
+}
+
+// End of protos/perfetto/metrics/android/lmk_reason_metric.proto
+
+// Begin of protos/perfetto/metrics/android/mem_metric.proto
+
+// Memory metrics on Android.
+message AndroidMemoryMetric {
+  message ProcessMetrics {
+    optional string process_name = 1;
+    optional ProcessMemoryCounters total_counters = 2;
+    repeated PriorityBreakdown priority_breakdown = 3;
+  }
+
+  message PriorityBreakdown {
+    optional string priority = 1;
+    optional ProcessMemoryCounters counters = 2;
+  }
+
+  message ProcessMemoryCounters {
+    optional Counter anon_rss = 1;
+    optional Counter file_rss = 2;
+    optional Counter swap = 3;
+    optional Counter anon_and_swap = 4;
+
+    // Available when ART trace events are available.
+    optional Counter java_heap = 5;
+  }
+
+  message Counter {
+    optional double min = 1;
+    optional double max = 2;
+    optional double avg = 3;
+  }
+
+  // Process metrics, grouped by process name
+  repeated ProcessMetrics process_metrics = 1;
+}
+
+// End of protos/perfetto/metrics/android/mem_metric.proto
+
+// Begin of protos/perfetto/metrics/android/mem_unagg_metric.proto
+
+// Unaggregated memory metrics on Android.
+message AndroidMemoryUnaggregatedMetric {
+  message ProcessValues {
+    optional string process_name = 1;
+    optional ProcessMemoryValues mem_values = 2;
+  }
+
+  message ProcessMemoryValues {
+    repeated Value anon_rss = 1;
+    repeated Value file_rss = 2;
+    repeated Value swap = 3;
+    repeated Value anon_and_swap = 4;
+  }
+
+  message Value {
+    optional int64 ts = 1;
+    optional int32 oom_score = 2;
+    optional double value = 3;
+  }
+
+  // Process metrics for every process instance in trace.
+  repeated ProcessValues process_values = 1;
+}
+
+// End of protos/perfetto/metrics/android/mem_unagg_metric.proto
+
 // Begin of protos/perfetto/metrics/android/package_list.proto
 
 message AndroidPackageList {
@@ -508,13 +497,193 @@
 
 // End of protos/perfetto/metrics/android/package_list.proto
 
+// Begin of protos/perfetto/metrics/android/powrails_metric.proto
+
+message AndroidPowerRails {
+  // Energy data per Power Rail at given ts.
+  message EnergyData {
+    // Time since device boot(CLOCK_BOTTOMTIME) in milli-seconds.
+    optional int64 timestamp_ms = 1;
+    // Accumulated energy since device boot in microwatt-seconds(uws).
+    optional double energy_uws = 2;
+  }
+
+  message PowerRails {
+    // Name of the rail.
+    optional string name = 1;
+    // Energy data for given rail and for all samples in the trace.
+    repeated EnergyData energy_data = 2;
+  }
+
+  // Energy data per Power Rail.
+  repeated PowerRails power_rails = 1;
+}
+// End of protos/perfetto/metrics/android/powrails_metric.proto
+
+// Begin of protos/perfetto/metrics/android/startup_metric.proto
+
+// Android app startup metrics.
+message AndroidStartupMetric {
+  // A simplified view of the task state durations for a thread
+  // and a span of time.
+  message TaskStateBreakdown {
+    optional int64 running_dur_ns = 1;
+    optional int64 runnable_dur_ns = 2;
+    optional int64 uninterruptible_sleep_dur_ns = 3;
+    optional int64 interruptible_sleep_dur_ns = 4;
+  }
+
+  message Slice {
+    optional int64 dur_ns = 1;
+    optional double dur_ms = 2;
+  }
+
+  // Timing information spanning the intent received by the
+  // activity manager to the first frame drawn.
+  // Next id: 21.
+  message ToFirstFrame {
+    optional int64 dur_ns = 1;
+    optional double dur_ms = 17;
+    optional TaskStateBreakdown main_thread_by_task_state = 2;
+
+    // In this timespan, how many processes (apart from the main activity) were
+    // spawned.
+    optional uint32 other_processes_spawned_count = 3;
+
+    // Total time spent in activity manager between the initial intent
+    // and the end of the activity starter.
+    optional Slice time_activity_manager = 4;
+
+    // The following slices follow the typical steps post-fork.
+    optional Slice time_activity_thread_main = 5;
+    optional Slice time_bind_application = 6;
+    optional Slice time_activity_start = 7;
+    optional Slice time_activity_resume = 8;
+    optional Slice time_choreographer = 9;
+
+    // If we are starting a new process, record the duration from the
+    // intent being received to the time we call the zygote.
+    optional Slice time_before_start_process = 10;
+
+    // The actual duration of the process start (based on the zygote slice).
+    optional Slice time_during_start_process = 11;
+
+    optional Slice to_post_fork = 18;
+    optional Slice to_activity_thread_main = 19;
+    optional Slice to_bind_application = 20;
+
+    optional Slice time_post_fork = 16;
+
+    // Deprecated was other_process_to_activity_cpu_ratio
+    reserved 12;
+
+    // Removed: was uint32 versions of to_post_fork, to_activity_thread_main and
+    // to_bind_application.
+    reserved 13, 14, 15;
+  }
+
+  // Metrics about startup which were developed by looking at experiments using
+  // high-speed cameras (HSC).
+  message HscMetrics {
+    // The duration of the full "startup" as defined by HSC tests.
+    optional Slice full_startup = 1;
+  }
+
+  // Next id: 8
+  message Startup {
+    // Random id uniquely identifying an app startup in this trace.
+    optional uint32 startup_id = 1;
+
+    // Name of the package launched
+    optional string package_name = 2;
+
+    // Name of the process launched
+    optional string process_name = 3;
+
+    // Did we ask the zygote for a new process
+    optional bool zygote_new_process = 4;
+
+    // Number of processes hosting the activity involved in the launch.
+    // This will usually be 1. If it is 0, it is indicative of a data / process
+    // error. If > 1, the process died during startup and the system respawned
+    // it.
+    optional uint32 activity_hosting_process_count = 6;
+
+    optional ToFirstFrame to_first_frame = 5;
+
+    // Details about the process (uid, version, etc)
+    optional AndroidProcessMetadata process = 7;
+
+    optional HscMetrics hsc = 8;
+  }
+
+  repeated Startup startup = 1;
+}
+
+// End of protos/perfetto/metrics/android/startup_metric.proto
+
+// Begin of protos/perfetto/metrics/android/task_names.proto
+
+message AndroidTaskNames {
+  message Process {
+    optional int64 pid = 1;
+
+    // Process name.
+    optional string process_name = 2;
+
+    // Names of all threads for this process.
+    repeated string thread_name = 3;
+
+    // User id under which this process runs.
+    optional int64 uid = 4;
+
+    // Packages matching the process uid.
+    repeated string uid_package_name = 5;
+  }
+
+  repeated Process process = 1;
+}
+
+// End of protos/perfetto/metrics/android/task_names.proto
+
+// Begin of protos/perfetto/metrics/android/thread_time_in_state_metric.proto
+
+message AndroidThreadTimeInStateMetric {
+  message MetricsByCoreType {
+    optional string core_type = 1;
+    optional int64 runtime_ms = 2;
+  }
+
+  message Thread {
+    optional string name = 1;
+    repeated MetricsByCoreType metrics_by_core_type = 2;
+  }
+
+  message Process {
+    optional AndroidProcessMetadata metadata = 1;
+    repeated MetricsByCoreType metrics_by_core_type = 2;
+    repeated Thread threads = 3;
+  }
+
+  repeated Process processes = 1;
+}
+
+// End of protos/perfetto/metrics/android/thread_time_in_state_metric.proto
+
 // Begin of protos/perfetto/metrics/android/unmapped_java_symbols.proto
 
 message UnmappedJavaSymbols {
+  message Field {
+    optional string field_name = 1;
+    optional string field_type_name = 2;
+  }
+
   message ProcessSymbols {
+    reserved 3;
+
     optional AndroidProcessMetadata process_metadata = 1;
     repeated string type_name = 2;
-    repeated string field_name = 3;
+    repeated Field field = 4;
   }
 
   repeated ProcessSymbols process_symbols = 1;
@@ -536,28 +705,6 @@
 
 // End of protos/perfetto/metrics/android/unsymbolized_frames.proto
 
-// Begin of protos/perfetto/metrics/android/java_heap_stats.proto
-
-message JavaHeapStats {
-  message Sample {
-    optional int64 ts = 1;
-    optional int64 heap_size = 2;
-    optional int64 reachable_heap_size = 3;
-  }
-
-  // Heap stats per process. One sample per dump (can be > 1 if continuous
-  // dump is enabled).
-  message InstanceStats {
-    optional uint32 upid = 1;
-    optional AndroidProcessMetadata process = 2;
-    repeated Sample samples = 3;
-  }
-
-  repeated InstanceStats instance_stats = 1;
-}
-
-// End of protos/perfetto/metrics/android/java_heap_stats.proto
-
 // Begin of protos/perfetto/metrics/metrics.proto
 
 // Trace processor metadata
@@ -574,11 +721,12 @@
   optional string android_build_fingerprint = 4;
   optional int64 statsd_triggering_subscription_id = 5;
   optional int64 trace_size_bytes = 6;
+  repeated string trace_trigger = 7;
 }
 
 // Root message for all Perfetto-based metrics.
 //
-// Next id: 21
+// Next id: 25
 message TraceMetrics {
   reserved 4, 10, 13, 14;
 
@@ -624,6 +772,9 @@
   // If the trace contains a heap graph, output allocation statistics.
   optional JavaHeapStats java_heap_stats = 17;
 
+  // If the trace contains a heap graph, output histogram.
+  optional JavaHeapHistogram java_heap_histogram = 21;
+
   // Metrics used to find potential culprits of low-memory kills.
   optional AndroidLmkReasonMetric android_lmk_reason = 18;
 
@@ -632,6 +783,12 @@
 
   optional AndroidHwuiMetric android_hwui_metric = 20;
 
+  optional AndroidDisplayMetrics display_metrics = 22;
+
+  optional AndroidTaskNames android_task_names = 23;
+
+  optional AndroidThreadTimeInStateMetric android_thread_time_in_state = 24;
+
   // Demo extensions.
   extensions 450 to 499;
 
diff --git a/protos/perfetto/trace/BUILD.gn b/protos/perfetto/trace/BUILD.gn
index ebcdfde..b644aaf 100644
--- a/protos/perfetto/trace/BUILD.gn
+++ b/protos/perfetto/trace/BUILD.gn
@@ -87,15 +87,14 @@
     "profiling:@TYPE@",
     "ps:@TYPE@",
     "sys_stats:@TYPE@",
+    "system_info:@TYPE@",
     "track_event:@TYPE@",
   ]
   sources = proto_sources_non_minimal
 }
 
 perfetto_proto_library("minimal_@TYPE@") {
-  deps = [
-    "../config:@TYPE@",
-  ]
+  deps = [ "../config:@TYPE@" ]
   sources = proto_sources_minimal
 }
 
@@ -103,18 +102,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 +117,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..a43e446 100644
--- a/protos/perfetto/trace/android/BUILD.gn
+++ b/protos/perfetto/trace/android/BUILD.gn
@@ -15,13 +15,12 @@
 import("../../../../gn/proto_library.gni")
 
 perfetto_proto_library("@TYPE@") {
-  deps = [
-    "../../common:@TYPE@",
-  ]
+  deps = [ "../../common:@TYPE@" ]
 
   sources = [
     "android_log.proto",
     "graphics_frame_event.proto",
+    "initial_display_state.proto",
     "packages_list.proto",
   ]
 }
diff --git a/src/trace_processor/destructible.cc b/protos/perfetto/trace/android/initial_display_state.proto
similarity index 68%
copy from src/trace_processor/destructible.cc
copy to protos/perfetto/trace/android/initial_display_state.proto
index 22bcf6a..09c1b73 100644
--- a/src/trace_processor/destructible.cc
+++ b/protos/perfetto/trace/android/initial_display_state.proto
@@ -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.
@@ -13,12 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include "src/trace_processor/destructible.h"
 
-namespace perfetto {
-namespace trace_processor {
+syntax = "proto2";
+package perfetto.protos;
 
-Destructible::~Destructible() = default;
-
-}  // namespace trace_processor
-}  // namespace perfetto
+message InitialDisplayState {
+  // Same values as android.view.Display.STATE_*
+  optional int32 display_state = 1;
+  optional double brightness = 2;
+}
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/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/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/all_protos.gni b/protos/perfetto/trace/ftrace/all_protos.gni
index a8ae6b4..8d58926 100644
--- a/protos/perfetto/trace/ftrace/all_protos.gni
+++ b/protos/perfetto/trace/ftrace/all_protos.gni
@@ -31,6 +31,7 @@
   "filemap.proto",
   "ftrace.proto",
   "i2c.proto",
+  "ion.proto",
   "ipi.proto",
   "irq.proto",
   "kmem.proto",
diff --git a/protos/perfetto/trace/ftrace/ftrace_event.proto b/protos/perfetto/trace/ftrace/ftrace_event.proto
index 38e4adc..5cb52de 100644
--- a/protos/perfetto/trace/ftrace/ftrace_event.proto
+++ b/protos/perfetto/trace/ftrace/ftrace_event.proto
@@ -31,6 +31,7 @@
 import "protos/perfetto/trace/ftrace/filemap.proto";
 import "protos/perfetto/trace/ftrace/ftrace.proto";
 import "protos/perfetto/trace/ftrace/i2c.proto";
+import "protos/perfetto/trace/ftrace/ion.proto";
 import "protos/perfetto/trace/ftrace/ipi.proto";
 import "protos/perfetto/trace/ftrace/irq.proto";
 import "protos/perfetto/trace/ftrace/kmem.proto";
@@ -412,5 +413,7 @@
     ZeroFtraceEvent zero = 331;
     GpuFrequencyFtraceEvent gpu_frequency = 332;
     SdeTracingMarkWriteFtraceEvent sde_tracing_mark_write = 333;
+    MarkVictimFtraceEvent mark_victim = 334;
+    IonStatFtraceEvent ion_stat = 335;
   }
 }
diff --git a/protos/perfetto/trace/ftrace/ion.proto b/protos/perfetto/trace/ftrace/ion.proto
new file mode 100644
index 0000000..03a34be
--- /dev/null
+++ b/protos/perfetto/trace/ftrace/ion.proto
@@ -0,0 +1,12 @@
+// Autogenerated by:
+// ../../tools/ftrace_proto_gen/ftrace_proto_gen.cc
+// Do not edit.
+
+syntax = "proto2";
+package perfetto.protos;
+
+message IonStatFtraceEvent {
+  optional uint32 buffer_id = 1;
+  optional int64 len = 2;
+  optional uint64 total_allocated = 3;
+}
diff --git a/protos/perfetto/trace/ftrace/oom.proto b/protos/perfetto/trace/ftrace/oom.proto
index 015f2da..a119196 100644
--- a/protos/perfetto/trace/ftrace/oom.proto
+++ b/protos/perfetto/trace/ftrace/oom.proto
@@ -10,3 +10,6 @@
   optional int32 oom_score_adj = 2;
   optional int32 pid = 3;
 }
+message MarkVictimFtraceEvent {
+  optional int32 pid = 1;
+}
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/vulkan_memory_event.proto b/protos/perfetto/trace/gpu/vulkan_memory_event.proto
index a8963cf..b66cafd 100644
--- a/protos/perfetto/trace/gpu/vulkan_memory_event.proto
+++ b/protos/perfetto/trace/gpu/vulkan_memory_event.proto
@@ -34,7 +34,7 @@
   }
 }
 
-// Each VulkanMemoryEvent encompasses information regarding one signle function
+// Each VulkanMemoryEvent encompasses information regarding one single function
 // call that results in reserving, binding or freeing host or GPU memory. There
 // is a special message type, ANNOTATIONS, which is used to communicate
 // information that are not directly related to a memory event, nonetheless are
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/perfetto/BUILD.gn b/protos/perfetto/trace/perfetto/BUILD.gn
index 5fc30a4..6cab649 100644
--- a/protos/perfetto/trace/perfetto/BUILD.gn
+++ b/protos/perfetto/trace/perfetto/BUILD.gn
@@ -17,5 +17,6 @@
 perfetto_proto_library("@TYPE@") {
   sources = [
     "perfetto_metatrace.proto",
+    "tracing_service_event.proto",
   ]
 }
diff --git a/protos/perfetto/trace/perfetto/tracing_service_event.proto b/protos/perfetto/trace/perfetto/tracing_service_event.proto
new file mode 100644
index 0000000..0028057
--- /dev/null
+++ b/protos/perfetto/trace/perfetto/tracing_service_event.proto
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+// Events emitted by the tracing service when data source states change.
+message TracingServiceEvent {
+  oneof event_type {
+    // When this is set to true, reports the point in time (the TracePacket's
+    // timestamp) when all data sources did see the start event and ACKed it.
+    // This identifies the point in time when it's safe to assume tha all data
+    // sources have been recording events.
+    // Note: like any other event in the trace, the byte offset at which this
+    // packet shows up in the trace is arbitrary and doesn't reflect causal
+    // ordering (i.e. it's okay to use this event to reason about the timestamps
+    // of othe packets, NOT their relative order w.r.t. this packet).
+    bool all_data_sources_started = 1;
+  }
+}
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 674423d..c30f496 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -11,64 +11,6 @@
 
 package perfetto.protos;
 
-// Begin of protos/perfetto/common/android_log_constants.proto
-
-// Values from NDK's android/log.h.
-enum AndroidLogId {
-  LID_DEFAULT = 0;  // MAIN.
-  LID_RADIO = 1;
-  LID_EVENTS = 2;
-  LID_SYSTEM = 3;
-  LID_CRASH = 4;
-  LID_STATS = 5;
-  LID_SECURITY = 6;
-  LID_KERNEL = 7;
-}
-
-enum AndroidLogPriority {
-  PRIO_UNSPECIFIED = 0;
-  PRIO_UNUSED = 1;  // _DEFAULT, but should never be seen in logs.
-  PRIO_VERBOSE = 2;
-  PRIO_DEBUG = 3;
-  PRIO_INFO = 4;
-  PRIO_WARN = 5;
-  PRIO_ERROR = 6;
-  PRIO_FATAL = 7;
-}
-
-// End of protos/perfetto/common/android_log_constants.proto
-
-// Begin of protos/perfetto/common/data_source_descriptor.proto
-
-// This message is sent from Producer(s) to the tracing Service when registering
-// to advertise their capabilities. It describes the structure of tracing
-// protos that will be produced by the data source and the supported filters.
-message DataSourceDescriptor {
-  optional string name = 1;  // e.g., "linux.ftrace", "chromium.tracing"
-
-  // When true the data source is expected to ack the stop request through the
-  // NotifyDataSourceStopped() IPC. This field has been introduced after
-  // Android P in Jul 2018 and is not supported on older versions.
-  optional bool will_notify_on_stop = 2;
-
-  // When true the data source is expected to ack the start request through the
-  // NotifyDataSourceStarted() IPC. This field has been introduced after
-  // Android P in March 2019 and is not supported on older versions.
-  optional bool will_notify_on_start = 3;
-
-  // If true, opt into receiving the ClearIncrementalState() IPC. This should be
-  // set if the data source writes packets that refer to previous trace
-  // contents, and knows how to stop referring to the already-emitted data.
-  optional bool handles_incremental_state_clear = 4;
-
-  // Optional specification about available GPU counters.
-  optional GpuCounterDescriptor gpu_counter_descriptor = 5 [lazy = true];
-
-  optional TrackEventDescriptor track_event_descriptor = 6 [lazy = true];
-}
-
-// End of protos/perfetto/common/data_source_descriptor.proto
-
 // Begin of protos/perfetto/common/gpu_counter_descriptor.proto
 
 // Description of GPU counters.
@@ -178,6 +120,536 @@
 
 // End of protos/perfetto/common/gpu_counter_descriptor.proto
 
+// Begin of protos/perfetto/common/track_event_descriptor.proto
+
+message TrackEventCategory {
+  optional string name = 1;
+  optional string description = 2;
+  repeated string tags = 3;
+}
+
+message TrackEventDescriptor {
+  repeated TrackEventCategory available_categories = 1;
+}
+
+// End of protos/perfetto/common/track_event_descriptor.proto
+
+// Begin of protos/perfetto/common/data_source_descriptor.proto
+
+// This message is sent from Producer(s) to the tracing Service when registering
+// to advertise their capabilities. It describes the structure of tracing
+// protos that will be produced by the data source and the supported filters.
+message DataSourceDescriptor {
+  optional string name = 1;  // e.g., "linux.ftrace", "chromium.tracing"
+
+  // When true the data source is expected to ack the stop request through the
+  // NotifyDataSourceStopped() IPC. This field has been introduced after
+  // Android P in Jul 2018 and is not supported on older versions.
+  optional bool will_notify_on_stop = 2;
+
+  // When true the data source is expected to ack the start request through the
+  // NotifyDataSourceStarted() IPC. This field has been introduced after
+  // Android P in March 2019 and is not supported on older versions.
+  optional bool will_notify_on_start = 3;
+
+  // If true, opt into receiving the ClearIncrementalState() IPC. This should be
+  // set if the data source writes packets that refer to previous trace
+  // contents, and knows how to stop referring to the already-emitted data.
+  optional bool handles_incremental_state_clear = 4;
+
+  // Optional specification about available GPU counters.
+  optional GpuCounterDescriptor gpu_counter_descriptor = 5 [lazy = true];
+
+  optional TrackEventDescriptor track_event_descriptor = 6 [lazy = true];
+}
+
+// End of protos/perfetto/common/data_source_descriptor.proto
+
+// Begin of protos/perfetto/common/tracing_service_state.proto
+
+// Reports the state of the tracing service. Used to gather details about the
+// data sources connected.
+// See ConsumerPort::QueryServiceState().
+message TracingServiceState {
+  // Describes a producer process.
+  message Producer {
+    optional int32 id = 1;     // Unique ID of the producer (monotonic counter).
+    optional string name = 2;  // Typically matches the process name.
+    optional int32 uid = 3;    // Unix uid of the remote process.
+  }
+
+  // Describes a data source registered by a producer. Data sources are listed
+  // regardless of the fact that they are being used or not.
+  message DataSource {
+    // Descriptor passed by the data source when calling RegisterDataSource().
+    optional DataSourceDescriptor ds_descriptor = 1;
+
+    // ID of the producer, as per Producer.id.
+    optional int32 producer_id = 2;
+  }
+
+  // Lists all the producers connected.
+  repeated Producer producers = 1;
+
+  // Lists the data sources available.
+  repeated DataSource data_sources = 2;
+
+  // Total number of tracing sessions.
+  optional int32 num_sessions = 3;
+
+  // Number of tracing sessions in the started state. Always <= num_sessions.
+  optional int32 num_sessions_started = 4;
+}
+
+// End of protos/perfetto/common/tracing_service_state.proto
+
+// Begin of protos/perfetto/common/android_log_constants.proto
+
+// Values from NDK's android/log.h.
+enum AndroidLogId {
+  LID_DEFAULT = 0;  // MAIN.
+  LID_RADIO = 1;
+  LID_EVENTS = 2;
+  LID_SYSTEM = 3;
+  LID_CRASH = 4;
+  LID_STATS = 5;
+  LID_SECURITY = 6;
+  LID_KERNEL = 7;
+}
+
+enum AndroidLogPriority {
+  PRIO_UNSPECIFIED = 0;
+  PRIO_UNUSED = 1;  // _DEFAULT, but should never be seen in logs.
+  PRIO_VERBOSE = 2;
+  PRIO_DEBUG = 3;
+  PRIO_INFO = 4;
+  PRIO_WARN = 5;
+  PRIO_ERROR = 6;
+  PRIO_FATAL = 7;
+}
+
+// End of protos/perfetto/common/android_log_constants.proto
+
+// Begin of protos/perfetto/config/android/android_log_config.proto
+
+message AndroidLogConfig {
+  repeated AndroidLogId log_ids = 1;
+
+  reserved 2;  // Was |poll_ms|, deprecated.
+
+  // If set ignores all log messages whose prio is < the given value.
+  optional AndroidLogPriority min_prio = 3;
+
+  // If non-empty ignores all log messages whose tag doesn't match one of the
+  // specified values.
+  repeated string filter_tags = 4;
+}
+
+// End of protos/perfetto/config/android/android_log_config.proto
+
+// Begin of protos/perfetto/config/android/packages_list_config.proto
+
+// Data source that lists details (such as version code) about packages on an
+// Android device.
+message PackagesListConfig {
+  // If not empty, emit info about only the following list of package names
+  // (exact match, no regex). Otherwise, emit info about all packages.
+  repeated string package_name_filter = 1;
+}
+
+// End of protos/perfetto/config/android/packages_list_config.proto
+
+// Begin of protos/perfetto/config/chrome/chrome_config.proto
+
+message ChromeConfig {
+  optional string trace_config = 1;
+
+  // When enabled, the data source should only fill in fields in the output that
+  // are not potentially privacy sensitive.
+  optional bool privacy_filtering_enabled = 2;
+}
+
+// End of protos/perfetto/config/chrome/chrome_config.proto
+
+// Begin of protos/perfetto/config/ftrace/ftrace_config.proto
+
+message FtraceConfig {
+  repeated string ftrace_events = 1;
+  repeated string atrace_categories = 2;
+  repeated string atrace_apps = 3;
+  // *Per-CPU* buffer size.
+  optional uint32 buffer_size_kb = 10;
+  optional uint32 drain_period_ms = 11;
+
+  // Configuration for compact encoding of scheduler events. When enabled (and
+  // recording the relevant ftrace events), specific high-volume events are
+  // encoded in a denser format than normal.
+  message CompactSchedConfig {
+    // If true, and sched_switch or sched_waking ftrace events are enabled,
+    // record those events in the compact format.
+    optional bool enabled = 1;
+  }
+  optional CompactSchedConfig compact_sched = 12;
+}
+
+// End of protos/perfetto/config/ftrace/ftrace_config.proto
+
+// Begin of protos/perfetto/config/gpu/gpu_counter_config.proto
+
+message GpuCounterConfig {
+  // Desired sampling interval for counters.
+  optional uint64 counter_period_ns = 1;
+
+  // List of counters to be sampled. Counter IDs correspond to the ones
+  // described in GpuCounterSpec in the data source descriptor.
+  repeated uint32 counter_ids = 2;
+
+  // Sample counters by instrumenting command buffers.
+  optional bool instrumented_sampling = 3;
+
+  // Fix gpu clock rate during trace session.
+  optional bool fix_gpu_clock = 4;
+}
+
+// End of protos/perfetto/config/gpu/gpu_counter_config.proto
+
+// Begin of protos/perfetto/config/gpu/vulkan_memory_config.proto
+
+message VulkanMemoryConfig {
+  // Tracking driver memory usage events
+  optional bool track_driver_memory_usage = 1;
+
+  // Tracking device memory usage events
+  optional bool track_device_memory_usage = 2;
+}
+
+// End of protos/perfetto/config/gpu/vulkan_memory_config.proto
+
+// Begin of protos/perfetto/config/inode_file/inode_file_config.proto
+
+message InodeFileConfig {
+  message MountPointMappingEntry {
+    optional string mountpoint = 1;
+    repeated string scan_roots = 2;
+  }
+
+  // How long to pause between batches.
+  optional uint32 scan_interval_ms = 1;
+
+  // How long to wait before the first scan in order to accumulate inodes.
+  optional uint32 scan_delay_ms = 2;
+
+  // How many inodes to scan in one batch.
+  optional uint32 scan_batch_size = 3;
+
+  // Do not scan for inodes not found in the static map.
+  optional bool do_not_scan = 4;
+
+  // If non-empty, only scan inodes corresponding to block devices named in
+  // this list.
+  repeated string scan_mount_points = 5;
+
+  // When encountering an inode belonging to a block device corresponding
+  // to one of the mount points in this map, scan its scan_roots instead.
+  repeated MountPointMappingEntry mount_point_mapping = 6;
+}
+
+// End of protos/perfetto/config/inode_file/inode_file_config.proto
+
+// Begin of protos/perfetto/config/power/android_power_config.proto
+
+message AndroidPowerConfig {
+  enum BatteryCounters {
+    BATTERY_COUNTER_UNSPECIFIED = 0;
+    BATTERY_COUNTER_CHARGE = 1;            // Coulomb counter.
+    BATTERY_COUNTER_CAPACITY_PERCENT = 2;  // Charge (%).
+    BATTERY_COUNTER_CURRENT = 3;           // Instantaneous current.
+    BATTERY_COUNTER_CURRENT_AVG = 4;       // Avg current.
+  }
+  optional uint32 battery_poll_ms = 1;
+  repeated BatteryCounters battery_counters = 2;
+
+  // Where available enables per-power-rail measurements.
+  optional bool collect_power_rails = 3;
+}
+
+// End of protos/perfetto/config/power/android_power_config.proto
+
+// Begin of protos/perfetto/config/process_stats/process_stats_config.proto
+
+message ProcessStatsConfig {
+  enum Quirks {
+    QUIRKS_UNSPECIFIED = 0;
+
+    // This has been deprecated and ignored as per 2018-05-01. Full scan at
+    // startup is now disabled by default and can be re-enabled using the
+    // |scan_all_processes_on_start| arg.
+    DISABLE_INITIAL_DUMP = 1 [deprecated = true];
+
+    DISABLE_ON_DEMAND = 2;
+  }
+
+  repeated Quirks quirks = 1;
+
+  // If enabled all processes will be scanned and dumped when the trace starts.
+  optional bool scan_all_processes_on_start = 2;
+
+  // If enabled thread names are also recoded (this is redundant if sched_switch
+  // is enabled).
+  optional bool record_thread_names = 3;
+
+  // If > 0 samples counters (see process_stats.proto) from
+  // /proc/pid/status and oom_score_adj every X ms.
+  // This is required to be > 100ms to avoid excessive CPU usage.
+  // TODO(primiano): add CPU cost for change this value.
+  optional uint32 proc_stats_poll_ms = 4;
+
+  // If empty samples stats for all processes. If non empty samples stats only
+  // for processes matching the given string in their argv0 (i.e. the first
+  // entry of /proc/pid/cmdline).
+  // TODO(primiano): implement this feature.
+  // repeated string proc_stats_filter = 5;
+
+  // This is required to be either = 0 or a multiple of |proc_stats_poll_ms|
+  // (default: |proc_stats_poll_ms|). If = 0, will be set to
+  // |proc_stats_poll_ms|. Non-multiples will be rounded down to the nearest
+  // multiple.
+  optional uint32 proc_stats_cache_ttl_ms = 6;
+
+  // Whether to record /proc/tid/time_in_state.
+  optional bool record_thread_time_in_state = 7;
+
+  // Size of the cache for thread time_in_state cpu freq values.
+  // If not specificed, the default is used.
+  optional uint32 thread_time_in_state_cache_size = 8;
+}
+
+// End of protos/perfetto/config/process_stats/process_stats_config.proto
+
+// Begin of protos/perfetto/config/profiling/heapprofd_config.proto
+
+// Configuration for go/heapprofd.
+// Next id: 19
+message HeapprofdConfig {
+  message ContinuousDumpConfig {
+    // ms to wait before first dump.
+    optional uint32 dump_phase_ms = 5;
+    // ms to wait between following dumps.
+    optional uint32 dump_interval_ms = 6;
+  }
+
+  // Set to 1 for perfect accuracy.
+  // Otherwise, sample every sample_interval_bytes on average.
+  //
+  // See https://docs.perfetto.dev/#/heapprofd?id=sampling-interval for more
+  // details.
+  optional uint64 sampling_interval_bytes = 1;
+
+  // E.g. surfaceflinger, com.android.phone
+  // This input is normalized in the following way: if it contains slashes,
+  // everything up to the last slash is discarded. If it contains "@",
+  // everything after the first @ is discared.
+  // E.g. /system/bin/surfaceflinger@1.0 normalizes to surfaceflinger.
+  // This transformation is also applied to the processes' command lines when
+  // matching.
+  repeated string process_cmdline = 2;
+
+  // For watermark based triggering or local debugging.
+  repeated uint64 pid = 4;
+
+  // Profile all processes eligible for profiling on the system.
+  // See https://docs.perfetto.dev/#/heapprofd?id=target-processes for which
+  // processes are eligible.
+  //
+  // On unmodified userdebug builds, this will lead to system crashes. Zygote
+  // will crash when trying to launch a new process as it will have an
+  // unexpected open socket to heapprofd.
+  //
+  // heapprofd will likely be overloaded by the amount of data for low
+  // sampling intervals.
+  optional bool all = 5;
+
+  // Do not profile processes whose anon RSS + swap < given value.
+  optional uint32 min_anonymous_memory_kb = 15;
+
+  // Stop profile if heapprofd memory usage goes beyond the given value.
+  optional uint32 max_heapprofd_memory_kb = 16;
+
+  // Stop profile if heapprofd CPU time since start of this data-source
+  // goes beyond given value.
+  optional uint64 max_heapprofd_cpu_secs = 17;
+
+  // Do not emit function names for mappings starting with this prefix.
+  // E.g. /system to not emit symbols for any system libraries.
+  repeated string skip_symbol_prefix = 7;
+
+  // Dump at a predefined interval.
+  optional ContinuousDumpConfig continuous_dump_config = 6;
+
+  // Size of the shared memory buffer between the profiled processes and
+  // heapprofd. Defaults to 8 MiB. If larger than 500 MiB, truncated to 500
+  // MiB.
+  //
+  // Needs to be:
+  // * at least 8192,
+  // * a power of two,
+  // * a multiple of 4096.
+  optional uint64 shmem_size_bytes = 8;
+
+  // When the shmem buffer is full, block the client instead of ending the
+  // trace. Use with caution as this will significantly slow down the target
+  // process.
+  optional bool block_client = 9;
+
+  // If set, stop the trace session after blocking the client for this
+  // timeout. Needs to be larger than 100 us, otherwise no retries are done.
+  optional uint32 block_client_timeout_us = 14;
+
+  // Do not profile processes from startup, only match already running
+  // processes.
+  //
+  // Can not be set at the same time as no_running.
+  optional bool no_startup = 10;
+
+  // Do not profile running processes. Only match processes on startup.
+  //
+  // Can not be set at the same time as no_startup.
+  optional bool no_running = 11;
+
+  // Gather information on how many bytes of allocations are on non-referenced
+  // pages. The way to use this generally is:
+  // 1. Start profile of app.
+  // 2. Start app.
+  // 3. Trigger a dump by sending SIGUSR1 to heapprofd.
+  // 4. Do operations.
+  // 5. End profile.
+  //
+  // You can then find the allocations that were not used for the operations you
+  // did in step 4.
+  optional bool idle_allocations = 12;
+
+  // Cause heapprofd to emit a single dump at the end, showing the memory usage
+  // at the point in time when the sampled heap usage of the process was at its
+  // maximum. This causes ProfilePacket.HeapSample.self_max to be set, and
+  // self_allocated and self_freed to not be set.
+  optional bool dump_at_max = 13;
+
+  // FEATURE FLAGS. THERE BE DRAGONS.
+
+  // Escape hatch if the session is being torn down because of a forked child
+  // that shares memory space, but is not correctly identified as a vforked
+  // child.
+  optional bool disable_fork_teardown = 18;
+}
+
+// End of protos/perfetto/config/profiling/heapprofd_config.proto
+
+// Begin of protos/perfetto/config/profiling/java_hprof_config.proto
+
+// Configuration for go/heapprofd.
+message JavaHprofConfig {
+  // If dump_interval_ms != 0, the following configuration is used.
+  message ContinuousDumpConfig {
+    // ms to wait before first continuous dump.
+    // A dump is always created at the beginning of the trace.
+    optional uint32 dump_phase_ms = 1;
+    // ms to wait between following dumps.
+    optional uint32 dump_interval_ms = 2;
+  }
+
+  // This input is normalized in the following way: if it contains slashes,
+  // everything up to the last slash is discarded. If it contains "@",
+  // everything after the first @ is discared.
+  // E.g. /system/bin/surfaceflinger@1.0 normalizes to surfaceflinger.
+  // This transformation is also applied to the processes' command lines when
+  // matching.
+  repeated string process_cmdline = 1;
+
+  // For watermark based triggering or local debugging.
+  repeated uint64 pid = 2;
+
+  // Dump at a predefined interval.
+  optional ContinuousDumpConfig continuous_dump_config = 3;
+
+  // Do not profile processes whose anon RSS + swap < given value.
+  optional uint32 min_anonymous_memory_kb = 4;
+
+  // Include the process' /proc/self/smaps.
+  // This only shows maps that:
+  // * start with /system
+  // * start with /vendor
+  // * start with /data/app
+  // * contain "extracted in memory from Y", where Y matches any of the above
+  optional bool dump_smaps = 5;
+}
+
+// End of protos/perfetto/config/profiling/java_hprof_config.proto
+
+// Begin of protos/perfetto/config/profiling/perf_event_config.proto
+
+// Configuration for the traced_perf profiler.
+//
+// At the time of writing, the config options are restricted to the periodic
+// system-wide stack sampling use-case (|all_cpus| must be true).
+message PerfEventConfig {
+  // If true, sample events on all CPUs.
+  optional bool all_cpus = 1;
+
+  // Per-cpu sampling frequency (requested from the kernel). Not guaranteed to
+  // be honored as the kernel can throttle the sampling rate if it's too high.
+  // If unset, an implementation-defined default is used.
+  optional uint32 sampling_frequency = 2;
+
+  // How often the per-cpu ring buffers are read by the producer.
+  // If unset, an implementation-defined default is used.
+  optional uint32 ring_buffer_read_period_ms = 8;
+
+  // Size (in 4k pages) of each per-cpu ring buffer that is filled by the
+  // kernel. If set, must be a power of two.
+  // If unset, an implementation-defined default is used.
+  optional uint32 ring_buffer_pages = 3;
+
+  // Process ID (TGID) whitelist. If this list is not empty, only matching
+  // samples will be retained. If multiple whitelists and blacklists are
+  // specified by the config, then all of them are evaluated for each sampled
+  // process.
+  repeated int32 target_pid = 4;
+
+  // Command line whitelist, matched against the
+  // /proc/<pid>/cmdline (not the comm string), with both sides being
+  // "normalized". Normalization is as follows: (1) trim everything beyond the
+  // first null or "@" byte; (2) if the string contains forward slashes, trim
+  // everything up to and including the last one.
+  repeated string target_cmdline = 5;
+
+  // PID blacklist.
+  repeated int32 exclude_pid = 6;
+
+  // Command line blacklist. Normalized in the same way as |target_cmdline|.
+  repeated string exclude_cmdline = 7;
+
+  ////////////////////
+  // Uncommon options:
+
+  // Timeout for the remote /proc/<pid>/{maps,mem} file descriptors for a
+  // sampled process. This is primarily for Android, where this lookup is
+  // asynchronous. As long as the producer is waiting, the associated samples
+  // will be kept enqueued (putting pressure on the capacity of the shared
+  // unwinding queue). Once a lookup for a process expires, all associated
+  // samples are discarded. However, if the lookup still succeeds after the
+  // timeout, future samples will be handled normally.
+  // If unset, an implementation-defined default is used.
+  optional uint32 remote_descriptor_timeout_ms = 9;
+
+  // Optional period for clearing state cached by the unwinder. This is a heavy
+  // operation that is only necessary for traces that target a wide set of
+  // processes, and require the memory footprint to be reset periodically.
+  // If unset, the cached state will not be cleared.
+  optional uint32 unwind_state_clear_period_ms = 10;
+}
+
+// End of protos/perfetto/config/profiling/perf_event_config.proto
+
 // Begin of protos/perfetto/common/sys_stats_counters.proto
 
 // When editing entries here remember also to update "sys_stats_counters.h" with
@@ -322,6 +794,549 @@
 }
 // End of protos/perfetto/common/sys_stats_counters.proto
 
+// Begin of protos/perfetto/config/sys_stats/sys_stats_config.proto
+
+// This file defines the configuration for the Linux /proc poller data source,
+// which injects counters in the trace.
+// Counters that are needed in the trace must be explicitly listed in the
+// *_counters fields. This is to avoid spamming the trace with all counters
+// at all times.
+// The sampling rate is configurable. All polling rates (*_period_ms) need
+// to be integer multiples of each other.
+// OK:     [10ms, 10ms, 10ms],  [10ms, 20ms, 10ms],  [10ms, 20ms, 60ms]
+// Not OK: [10ms, 10ms, 11ms],  [10ms, 15ms, 20ms]
+message SysStatsConfig {
+  // Polls /proc/meminfo every X ms, if non-zero.
+  // This is required to be > 10ms to avoid excessive CPU usage.
+  // Cost: 0.3 ms [read] + 0.07 ms [parse + trace injection]
+  optional uint32 meminfo_period_ms = 1;
+
+  // If empty all known counters are reported. Otherwise, only the counters
+  // specified below are reported.
+  repeated MeminfoCounters meminfo_counters = 2;
+
+  // Polls /proc/vmstat every X ms, if non-zero.
+  // This is required to be > 10ms to avoid excessive CPU usage.
+  // Cost: 0.2 ms [read] + 0.3 ms [parse + trace injection]
+  optional uint32 vmstat_period_ms = 3;
+  repeated VmstatCounters vmstat_counters = 4;
+
+  // Pols /proc/stat every X ms, if non-zero.
+  // This is required to be > 10ms to avoid excessive CPU usage.
+  // Cost: 4.1 ms [read] + 1.9 ms [parse + trace injection]
+  optional uint32 stat_period_ms = 5;
+  enum StatCounters {
+    STAT_UNSPECIFIED = 0;
+    STAT_CPU_TIMES = 1;
+    STAT_IRQ_COUNTS = 2;
+    STAT_SOFTIRQ_COUNTS = 3;
+    STAT_FORK_COUNT = 4;
+  }
+  repeated StatCounters stat_counters = 6;
+}
+
+// End of protos/perfetto/config/sys_stats/sys_stats_config.proto
+
+// Begin of protos/perfetto/config/test_config.proto
+
+// The configuration for a fake producer used in tests.
+message TestConfig {
+  message DummyFields {
+    optional uint32 field_uint32 = 1;
+    optional int32 field_int32 = 2;
+    optional uint64 field_uint64 = 3;
+    optional int64 field_int64 = 4;
+    optional fixed64 field_fixed64 = 5;
+    optional sfixed64 field_sfixed64 = 6;
+    optional fixed32 field_fixed32 = 7;
+    optional sfixed32 field_sfixed32 = 8;
+    optional double field_double = 9;
+    optional float field_float = 10;
+    optional sint64 field_sint64 = 11;
+    optional sint32 field_sint32 = 12;
+    optional string field_string = 13;
+    optional bytes field_bytes = 14;
+  }
+
+  // The number of messages the fake producer should send.
+  optional uint32 message_count = 1;
+
+  // The maximum number of messages which should be sent each second.
+  // The actual obserced speed may be lower if the producer is unable to
+  // work fast enough.
+  // If this is zero or unset, the producer will send as fast as possible.
+  optional uint32 max_messages_per_second = 2;
+
+  // The seed value for a simple multiplicative congruential pseudo-random
+  // number sequence.
+  optional uint32 seed = 3;
+
+  // The size of each message in bytes. Should be greater than or equal 5 to
+  // account for the number of bytes needed to encode the random number and a
+  // null byte for the string.
+  optional uint32 message_size = 4;
+
+  // Whether the producer should send a event batch when the data source is
+  // is initially registered.
+  optional bool send_batch_on_register = 5;
+
+  optional DummyFields dummy_fields = 6;
+}
+
+// End of protos/perfetto/config/test_config.proto
+
+// Begin of protos/perfetto/config/track_event/track_event_config.proto
+
+message TrackEventConfig {
+  // The following fields define the set of enabled trace categories. Each list
+  // item is a glob.
+  //
+  // To determine if category is enabled, it is checked against the filters in
+  // the following order:
+  //
+  //   1. Exact matches in enabled categories.
+  //   2. Exact matches in enabled tags.
+  //   3. Exact matches in disabled categories.
+  //   4. Exact matches in disabled tags.
+  //   5. Pattern matches in enabled categories.
+  //   6. Pattern matches in enabled tags.
+  //   7. Pattern matches in disabled categories.
+  //   8. Pattern matches in disabled tags.
+  //
+  // If none of the steps produced a match, the category is enabled by default.
+  //
+  // Examples:
+  //
+  //  - To enable all non-slow/debug categories:
+  //
+  //       No configuration needed, happens by default.
+  //
+  //  - To enable a specific category:
+  //
+  //       disabled_categories = ["*"]
+  //       enabled_categories = ["my_category"]
+  //
+  //  - To enable only categories with a specific tag:
+  //
+  //       disabled_tags = ["*"]
+  //       enabled_tags = ["my_tag"]
+  //
+  repeated string disabled_categories = 1;  // Default: []
+  repeated string enabled_categories = 2;   // Default: []
+  repeated string disabled_tags = 3;  // Default: [“slow”, “debug”]
+  repeated string enabled_tags = 4;   // Default: []
+}
+
+// End of protos/perfetto/config/track_event/track_event_config.proto
+
+// Begin of protos/perfetto/config/data_source_config.proto
+
+// The configuration that is passed to each data source when starting tracing.
+message DataSourceConfig {
+  // Data source unique name, e.g., "linux.ftrace". This must match
+  // the name passed by the data source when it registers (see
+  // RegisterDataSource()).
+  optional string name = 1;
+
+  // The index of the logging buffer where TracePacket(s) will be stored.
+  // This field doesn't make a major difference for the Producer(s). The final
+  // logging buffers, in fact, are completely owned by the Service. We just ask
+  // the Producer to copy this number into the chunk headers it emits, so that
+  // the Service can quickly identify the buffer where to move the chunks into
+  // without expensive lookups on its fastpath.
+  optional uint32 target_buffer = 2;
+
+  // Set by the service to indicate the duration of the trace.
+  // DO NOT SET in consumer as this will be overridden by the service.
+  optional uint32 trace_duration_ms = 3;
+
+  // Set by the service to indicate how long it waits after StopDataSource.
+  // DO NOT SET in consumer as this will be overridden by the service.
+  optional uint32 stop_timeout_ms = 7;
+
+  // Set by the service to indicate whether this tracing session has extra
+  // guardrails.
+  // DO NOT SET in consumer as this will be overridden by the service.
+  optional bool enable_extra_guardrails = 6;
+
+  // Set by the service to indicate which tracing session the data source
+  // belongs to. The intended use case for this is checking if two data sources,
+  // one of which produces metadata for the other one, belong to the same trace
+  // session and hence should be linked together.
+  // This field was introduced in Aug 2018 after Android P.
+  optional uint64 tracing_session_id = 4;
+
+  // Keeep the lower IDs (up to 99) for fields that are *not* specific to
+  // data-sources and needs to be processed by the traced daemon.
+
+  // All data source config fields must be marked as [lazy=true]. This prevents
+  // the proto-to-cpp generator from recursing into those when generating the
+  // cpp classes and polluting tracing/core with data-source-specific classes.
+  // Instead they are treated as opaque strings containing raw proto bytes.
+
+  // Data source name: linux.ftrace
+  optional FtraceConfig ftrace_config = 100 [lazy = true];
+  // Data source name: linux.inode_file_map
+  optional InodeFileConfig inode_file_config = 102 [lazy = true];
+  // Data source name: linux.process_stats
+  optional ProcessStatsConfig process_stats_config = 103 [lazy = true];
+  // Data source name: linux.sys_stats
+  optional SysStatsConfig sys_stats_config = 104 [lazy = true];
+  // Data source name: android.heapprofd
+  optional HeapprofdConfig heapprofd_config = 105 [lazy = true];
+  // Data source name: android.java_hprof
+  optional JavaHprofConfig java_hprof_config = 110 [lazy = true];
+  // Data source name: android.power
+  optional AndroidPowerConfig android_power_config = 106 [lazy = true];
+  // Data source name: android.log
+  optional AndroidLogConfig android_log_config = 107 [lazy = true];
+  // TODO(fmayer): Add data source name for this.
+  optional GpuCounterConfig gpu_counter_config = 108 [lazy = true];
+  // Data source name: android.packages_list
+  optional PackagesListConfig packages_list_config = 109 [lazy = true];
+  // Data source name: linux.perf
+  optional PerfEventConfig perf_event_config = 111 [lazy = true];
+  // Data source name: vulkan.memory_tracker
+  optional VulkanMemoryConfig vulkan_memory_config = 112 [lazy = true];
+  // Data source name: track_event
+  optional TrackEventConfig track_event_config = 113 [lazy = true];
+
+  // Chrome is special as it doesn't use the perfetto IPC layer. We want to
+  // avoid proto serialization and de-serialization there because that would
+  // just add extra hops on top of the Mojo ser/des. Instead we auto-generate a
+  // C++ class for it so it can pass around plain C++ objets.
+  optional ChromeConfig chrome_config = 101;
+
+  // This is a fallback mechanism to send a free-form text config to the
+  // producer. In theory this should never be needed. All the code that
+  // is part of the platform (i.e. traced service) is supposed to *not* truncate
+  // the trace config proto and propagate unknown fields. However, if anything
+  // in the pipeline (client or backend) ends up breaking this forward compat
+  // plan, this field will become the escape hatch to allow future data sources
+  // to get some meaningful configuration.
+  optional string legacy_config = 1000;
+
+  // This field is only used for testing.
+  optional TestConfig for_testing = 1001;
+
+  reserved 268435455;  // Was |for_testing|. Caused more problems then found.
+}
+
+// End of protos/perfetto/config/data_source_config.proto
+
+// Begin of protos/perfetto/config/trace_config.proto
+
+// The overall config that is used when starting a new tracing session through
+// ProducerPort::StartTracing().
+// It contains the general config for the logging buffer(s) and the configs for
+// all the data source being enabled.
+//
+// Next id: 30.
+message TraceConfig {
+  message BufferConfig {
+    optional uint32 size_kb = 1;
+
+    reserved 2;  // |page_size|, now deprecated.
+    reserved 3;  // |optimize_for|, now deprecated.
+
+    enum FillPolicy {
+      UNSPECIFIED = 0;
+
+      // Default behavior. The buffer operates as a conventional ring buffer.
+      // If the writer is faster than the reader (or if the reader reads only
+      // after tracing is stopped) newly written packets will overwrite old
+      // packets.
+      RING_BUFFER = 1;
+
+      // Behaves like RING_BUFFER as long as there is space in the buffer or
+      // the reader catches up with the writer. As soon as the writer hits
+      // an unread chunk, it stops accepting new data in the buffer.
+      DISCARD = 2;
+    }
+    optional FillPolicy fill_policy = 4;
+  }
+  repeated BufferConfig buffers = 1;
+
+  message DataSource {
+    // Filters and data-source specific config. It contains also the unique name
+    // of the data source, the one passed in the  DataSourceDescriptor when they
+    // register on the service.
+    optional protos.DataSourceConfig config = 1;
+
+    // Optional. If multiple producers (~processes) expose the same data source
+    // and either |producer_name_filter| or |producer_name_regex_filter| is set,
+    // the data source is enabled only for producers whose names match any of
+    // the filters.
+    // |producer_name_filter| has to be an exact match, while
+    // |producer_name_regex_filter| is a regular expression.
+    // This allows to enable a data source only for specific processes.
+    // The "repeated" fields have OR semantics: specifying a filter ["foo",
+    // "bar"] will enable data sources on both "foo" and "bar" (if they exist).
+    repeated string producer_name_filter = 2;
+    repeated string producer_name_regex_filter = 3;
+  }
+  repeated DataSource data_sources = 2;
+
+  // Config for disabling builtin data sources in the tracing service.
+  message BuiltinDataSource {
+    // Disable emitting clock timestamps into the trace.
+    optional bool disable_clock_snapshotting = 1;
+
+    // Disable echoing the original trace config in the trace.
+    optional bool disable_trace_config = 2;
+
+    // Disable emitting system info (build fingerprint, cpuinfo, etc).
+    optional bool disable_system_info = 3;
+
+    // Disable emitting events for data-source state changes (e.g. the marker
+    // for all data sources having ACKed the start of the trace).
+    optional bool disable_service_events = 4;
+  }
+  optional BuiltinDataSource builtin_data_sources = 20;
+
+  // If specified, the trace will be stopped |duration_ms| after starting.
+  // This does *not* count the time the system is suspended, so we will run
+  // for duration_ms of system activity, not wall time.
+  //
+  // However in case of traces with triggers, see
+  // TriggerConfig.trigger_timeout_ms instead.
+  optional uint32 duration_ms = 3;
+
+  // This is set when --dropbox is passed to the Perfetto command line client
+  // and enables guardrails that limit resource usage for traces requested
+  // by statsd.
+  optional bool enable_extra_guardrails = 4;
+
+  enum LockdownModeOperation {
+    LOCKDOWN_UNCHANGED = 0;
+    LOCKDOWN_CLEAR = 1;
+    LOCKDOWN_SET = 2;
+  }
+  // Reject producers that are not running under the same UID as the tracing
+  // service.
+  optional LockdownModeOperation lockdown_mode = 5;
+
+  message ProducerConfig {
+    // Identifies the producer for which this config is for.
+    optional string producer_name = 1;
+
+    // Specifies the preferred size of the shared memory buffer. If the size is
+    // larger than the max size, the max will be used. If it is smaller than
+    // the page size or doesn't fit pages evenly into it, it will fall back to
+    // the size specified by the producer or finally the default shared memory
+    // size.
+    optional uint32 shm_size_kb = 2;
+
+    // Specifies the preferred size of each page in the shared memory buffer.
+    // Must be an integer multiple of 4K.
+    optional uint32 page_size_kb = 3;
+  }
+
+  repeated ProducerConfig producers = 6;
+
+  // Contains statsd-specific metadata about an alert associated with the trace.
+  message StatsdMetadata {
+    // The identifier of the alert which triggered this trace.
+    optional int64 triggering_alert_id = 1;
+    // The uid which registered the triggering configuration with statsd.
+    optional int32 triggering_config_uid = 2;
+    // The identifier of the config which triggered the alert.
+    optional int64 triggering_config_id = 3;
+    // The identifier of the subscription which triggered this trace.
+    optional int64 triggering_subscription_id = 4;
+  }
+
+  // Statsd-specific metadata.
+  optional StatsdMetadata statsd_metadata = 7;
+
+  // When true && |output_path| is empty, the EnableTracing() request must
+  // provide a file descriptor. The service will then periodically read packets
+  // out of the trace buffer and store it into the passed file.
+  // If |output_path| is not empty no fd should be passed, the service
+  // will create a new file and write into that (see comment below).
+  optional bool write_into_file = 8;
+
+  // This must point to a non-existing file. If the file exists the service
+  // will NOT overwrite and will fail instead as a security precaution.
+  // On Android, when this is used with the system traced, the path must be
+  // within /data/misc/perfetto-traces/ or the trace will fail.
+  // This option has been introduced in Android R. Before R write_into_file
+  // can be used only with the "pass a file descriptor over IPC" mode.
+  optional string output_path = 29;
+
+  // Optional. If non-zero tunes the write period. A min value of 100ms is
+  // enforced (i.e. smaller values are ignored).
+  optional uint32 file_write_period_ms = 9;
+
+  // Optional. When non zero the periodic write stops once at most X bytes
+  // have been written into the file. Tracing is disabled when this limit is
+  // reached, even if |duration_ms| has not been reached yet.
+  optional uint64 max_file_size_bytes = 10;
+
+  // Contains flags which override the default values of the guardrails inside
+  // Perfetto. These values are only affect userdebug builds.
+  message GuardrailOverrides {
+    // Override the default limit (in bytes) for uploading data to server within
+    // a 24 hour period.
+    optional uint64 max_upload_per_day_bytes = 1;
+  }
+
+  optional GuardrailOverrides guardrail_overrides = 11;
+
+  // When true, data sources are not started until an explicit call to
+  // StartTracing() on the consumer port. This is to support early
+  // initialization and fast trace triggering. This can be used only when the
+  // Consumer explicitly triggers the StartTracing() method.
+  // This should not be used in a remote trace config via statsd, doing so will
+  // result in a hung trace session.
+  optional bool deferred_start = 12;
+
+  // When set, it periodically issues a Flush() to all data source, forcing them
+  // to commit their data into the tracing service. This can be used for
+  // quasi-real-time streaming mode and to guarantee some partial ordering of
+  // events in the trace in windows of X ms.
+  optional uint32 flush_period_ms = 13;
+
+  // Wait for this long for producers to acknowledge flush requests.
+  // Default 5s.
+  optional uint32 flush_timeout_ms = 14;
+
+  // Wait for this long for producers to acknowledge stop requests.
+  // Default 5s.
+  optional uint32 data_source_stop_timeout_ms = 23;
+
+  reserved 15;  // |disable_clock_snapshotting| moved.
+
+  // Android-only. If set, sends an intent to the Traceur system app when the
+  // trace ends to notify it about the trace readiness.
+  optional bool notify_traceur = 16;
+
+  // Triggers allow producers to start or stop the tracing session when an event
+  // occurs.
+  //
+  // For example if we are tracing probabilistically, most traces will be
+  // uninteresting. Triggers allow us to keep only the interesting ones such as
+  // those traces during which the device temperature reached a certain
+  // threshold. In this case the producer can activate a trigger to keep
+  // (STOP_TRACING) the trace, otherwise it can also begin a trace
+  // (START_TRACING) because it knows something is about to happen.
+  message TriggerConfig {
+    enum TriggerMode {
+      UNSPECIFIED = 0;
+
+      // When this mode is chosen, data sources are not started until one of the
+      // |triggers| are received. This supports early initialization and fast
+      // starting of the tracing system. On triggering, the session will then
+      // record for |stop_delay_ms|. However if no trigger is seen
+      // after |trigger_timeout_ms| the session will be stopped and no data will
+      // be returned.
+      START_TRACING = 1;
+
+      // When this mode is chosen, the session will be started via the normal
+      // EnableTracing() & StartTracing(). If no trigger is ever seen
+      // the session will be stopped after |trigger_timeout_ms| and no data will
+      // be returned. However if triggered the trace will stop after
+      // |stop_delay_ms| and any data in the buffer will be returned to the
+      // consumer.
+      STOP_TRACING = 2;
+    }
+    optional TriggerMode trigger_mode = 1;
+
+    message Trigger {
+      // The producer must specify this name to activate the trigger.
+      optional string name = 1;
+
+      // The a std::regex that will match the producer that can activate this
+      // trigger. This is optional. If unset any producers can activate this
+      // trigger.
+      optional string producer_name_regex = 2;
+
+      // After a trigger is received either in START_TRACING or STOP_TRACING
+      // mode then the trace will end |stop_delay_ms| after triggering.
+      optional uint32 stop_delay_ms = 3;
+    }
+    // A list of triggers which are related to this configuration. If ANY
+    // trigger is seen then an action will be performed based on |trigger_mode|.
+    repeated Trigger triggers = 2;
+
+    // Required and must be positive if a TriggerConfig is specified. This is
+    // how long this TraceConfig should wait for a trigger to arrive. After this
+    // period of time if no trigger is seen the TracingSession will be cleaned
+    // up.
+    optional uint32 trigger_timeout_ms = 3;
+  }
+  optional TriggerConfig trigger_config = 17;
+
+  // When this is non-empty the perfetto command line tool will ignore the rest
+  // of this TraceConfig and instead connect to the perfetto service as a
+  // producer and send these triggers, potentially stopping or starting traces
+  // that were previous configured to use a TriggerConfig.
+  repeated string activate_triggers = 18;
+
+  // Configuration for trace contents that reference earlier trace data. For
+  // example, a data source might intern strings, and emit packets containing
+  // {interned id : string} pairs. Future packets from that data source can then
+  // use the interned ids instead of duplicating the raw string contents. The
+  // trace parser will then need to use that interning table to fully interpret
+  // the rest of the trace.
+  message IncrementalStateConfig {
+    // If nonzero, notify eligible data sources to clear their incremental state
+    // periodically, with the given period. The notification is sent only to
+    // data sources that have |handles_incremental_state_clear| set in their
+    // DataSourceDescriptor. The notification requests that the data source
+    // stops referring to past trace contents. This is particularly useful when
+    // tracing in ring buffer mode, where it is not exceptional to overwrite old
+    // trace data.
+    //
+    // Warning: this time-based global clearing is likely to be removed in the
+    // future, to be replaced with a smarter way of sending the notifications
+    // only when necessary.
+    optional uint32 clear_period_ms = 1;
+  }
+  optional IncrementalStateConfig incremental_state_config = 21;
+
+  // Additional guardrail used by the Perfetto command line client.
+  // On user builds when --dropbox is set perfetto will refuse to trace unless
+  // this is also set.
+  // Added in Q.
+  optional bool allow_user_build_tracing = 19;
+
+  // If set the tracing service will ensure there is at most one tracing session
+  // with this key.
+  optional string unique_session_name = 22;
+
+  // Compress trace with the given method. Best effort.
+  enum CompressionType {
+    COMPRESSION_TYPE_UNSPECIFIED = 0;
+    COMPRESSION_TYPE_DEFLATE = 1;
+  }
+  optional CompressionType compression_type = 24;
+
+  // Android-only. Debug builds only. Not for general use. If set, saves a
+  // Dropbox trace into an incident. This field is read by perfetto_cmd, rather
+  // than the tracing service. All fields are mandatory.
+  message IncidentReportConfig {
+    optional string destination_package = 1;
+    optional string destination_class = 2;
+    // Level of filtering in the requested incident. See |Destination| in
+    // frameworks/base/core/proto/android/privacy.proto.
+    optional int32 privacy_level = 3;
+    // If true, do not write the trace into dropbox (i.e. incident only).
+    // Otherwise, write to both dropbox and incident.
+    optional bool skip_dropbox = 4;
+  }
+  optional IncidentReportConfig incident_report_config = 25;
+
+  // DEPRECATED. Was trace_uuid, use trace_uuid_msb and trace_uuid_lsb instead.
+  reserved 26;
+  // An identifier clients can use to tie this trace to other logging.
+  // Alternative encoding of trace_uuid as two int64s.
+  optional int64 trace_uuid_msb = 27;
+  optional int64 trace_uuid_lsb = 28;
+}
+
+// End of protos/perfetto/config/trace_config.proto
+
 // Begin of protos/perfetto/common/trace_stats.proto
 
 // Statistics for the internals of the tracing service.
@@ -468,52 +1483,6 @@
 
 // End of protos/perfetto/common/trace_stats.proto
 
-// Begin of protos/perfetto/common/tracing_service_state.proto
-
-// Reports the state of the tracing service. Used to gather details about the
-// data sources connected.
-// See ConsumerPort::QueryServiceState().
-message TracingServiceState {
-  // Describes a producer process.
-  message Producer {
-    optional int32 id = 1;     // Unique ID of the producer (monotonic counter).
-    optional string name = 2;  // Typically matches the process name.
-    optional int32 uid = 3;    // Unix uid of the remote process.
-  }
-
-  // Describes a data source registered by a producer. Data sources are listed
-  // regardless of the fact that they are being used or not.
-  message DataSource {
-    // Descriptor passed by the data source when calling RegisterDataSource().
-    optional DataSourceDescriptor ds_descriptor = 1;
-
-    // ID of the producer, as per Producer.id.
-    optional int32 producer_id = 2;
-  }
-
-  // Lists all the producers connected.
-  repeated Producer producers = 1;
-
-  // Lists the data sources available.
-  repeated DataSource data_sources = 2;
-
-  // Total number of tracing sessions.
-  optional int32 num_sessions = 3;
-
-  // Number of tracing sessions in the started state. Always <= num_sessions.
-  optional int32 num_sessions_started = 4;
-}
-
-// End of protos/perfetto/common/tracing_service_state.proto
-
-// Begin of protos/perfetto/common/track_event_descriptor.proto
-
-message TrackEventDescriptor {
-  repeated string available_categories = 1;
-}
-
-// End of protos/perfetto/common/track_event_descriptor.proto
-
 // Begin of protos/perfetto/trace/android/android_log.proto
 
 message AndroidLogPacket {
@@ -611,6 +1580,16 @@
 
 // End of protos/perfetto/trace/android/graphics_frame_event.proto
 
+// Begin of protos/perfetto/trace/android/initial_display_state.proto
+
+message InitialDisplayState {
+  // Same values as android.view.Display.STATE_*
+  optional int32 display_state = 1;
+  optional double brightness = 2;
+}
+
+// End of protos/perfetto/trace/android/initial_display_state.proto
+
 // Begin of protos/perfetto/trace/android/packages_list.proto
 
 message PackagesList {
@@ -731,6 +1710,114 @@
 
 // End of protos/perfetto/trace/chrome/chrome_metadata.proto
 
+// Begin of protos/perfetto/trace/chrome/chrome_trace_event.proto
+
+message ChromeTracedValue {
+  enum NestedType {
+    DICT = 0;
+    ARRAY = 1;
+  }
+  optional NestedType nested_type = 1;
+
+  repeated string dict_keys = 2;
+  repeated ChromeTracedValue dict_values = 3;
+  repeated ChromeTracedValue array_values = 4;
+  optional int32 int_value = 5;
+  optional double double_value = 6;
+  optional bool bool_value = 7;
+  optional string string_value = 8;
+}
+
+message ChromeStringTableEntry {
+  optional string value = 1;
+  optional int32 index = 2;
+}
+
+// Deprecated, use TrackEvent protos instead.
+message ChromeTraceEvent {
+  message Arg {
+    optional string name = 1;
+
+    oneof value {
+      bool bool_value = 2;
+      uint64 uint_value = 3;
+      int64 int_value = 4;
+      double double_value = 5;
+      string string_value = 6;
+      // Pointers are stored in a separate type as the JSON output treats them
+      // differently from other uint64 values.
+      uint64 pointer_value = 7;
+      string json_value = 8;
+      ChromeTracedValue traced_value = 10;
+    }
+
+    // Takes precedence over |name| if set,
+    // and is an index into |string_table|.
+    optional uint32 name_index = 9;
+  }
+
+  optional string name = 1;
+  optional int64 timestamp = 2;
+  optional int32 phase = 3;
+  optional int32 thread_id = 4;
+  optional int64 duration = 5;
+  optional int64 thread_duration = 6;
+  optional string scope = 7;
+  optional uint64 id = 8;
+  optional uint32 flags = 9;
+  optional string category_group_name = 10;
+  optional int32 process_id = 11;
+  optional int64 thread_timestamp = 12;
+  optional uint64 bind_id = 13;
+
+  repeated Arg args = 14;
+
+  // Takes precedence over respectively |name| and
+  // |category_group_name_index| if set,
+  // and are indices into |string_table|.
+  optional uint32 name_index = 15;
+  optional uint32 category_group_name_index = 16;
+}
+
+message ChromeMetadata {
+  optional string name = 1;
+
+  oneof value {
+    string string_value = 2;
+    bool bool_value = 3;
+    int64 int_value = 4;
+    string json_value = 5;
+  }
+}
+
+// Subtraces produced in legacy json format by Chrome tracing agents not yet
+// updated to support the new binary format, e.g. ETW and CrOS ARC.
+// TODO(eseckler): Update these agents to become perfetto producers.
+message ChromeLegacyJsonTrace {
+  enum TraceType {
+    USER_TRACE = 0;
+    SYSTEM_TRACE = 1;  // Deprecated.
+  }
+  optional TraceType type = 1;
+  optional string data = 2;
+}
+
+message ChromeEventBundle {
+  // Deprecated, use TrackEvent protos instead.
+  repeated ChromeTraceEvent trace_events = 1 [deprecated = true];
+  repeated ChromeMetadata metadata = 2;
+  // ftrace output from CrOS and Cast system tracing agents.
+  // TODO(eseckler): Replace system traces with native perfetto service.
+  repeated string legacy_ftrace_output = 4;
+  repeated ChromeLegacyJsonTrace legacy_json_trace = 5;
+
+  // Contents of a string table that's valid for
+  // the whole ChromeEventBundle entry.
+  repeated ChromeStringTableEntry string_table = 3 [deprecated = true];
+}
+
+// End of protos/perfetto/trace/chrome/chrome_trace_event.proto
+
 // Begin of protos/perfetto/trace/clock_snapshot.proto
 
 // A snapshot of clock readings to allow for trace alignment.
@@ -990,6 +2077,60 @@
 
 // End of protos/perfetto/trace/ftrace/block.proto
 
+// Begin of protos/perfetto/trace/ftrace/cgroup.proto
+
+message CgroupAttachTaskFtraceEvent {
+  optional int32 dst_root = 1;
+  optional int32 dst_id = 2;
+  optional int32 pid = 3;
+  optional string comm = 4;
+  optional string cname = 5;
+}
+message CgroupMkdirFtraceEvent {
+  optional int32 root = 1;
+  optional int32 id = 2;
+  optional string cname = 3;
+}
+message CgroupRemountFtraceEvent {
+  optional int32 root = 1;
+  optional uint32 ss_mask = 2;
+  optional string name = 3;
+}
+message CgroupRmdirFtraceEvent {
+  optional int32 root = 1;
+  optional int32 id = 2;
+  optional string cname = 3;
+}
+message CgroupTransferTasksFtraceEvent {
+  optional int32 dst_root = 1;
+  optional int32 dst_id = 2;
+  optional int32 pid = 3;
+  optional string comm = 4;
+  optional string cname = 5;
+}
+message CgroupDestroyRootFtraceEvent {
+  optional int32 root = 1;
+  optional uint32 ss_mask = 2;
+  optional string name = 3;
+}
+message CgroupReleaseFtraceEvent {
+  optional int32 root = 1;
+  optional int32 id = 2;
+  optional string cname = 3;
+}
+message CgroupRenameFtraceEvent {
+  optional int32 root = 1;
+  optional int32 id = 2;
+  optional string cname = 3;
+}
+message CgroupSetupRootFtraceEvent {
+  optional int32 root = 1;
+  optional uint32 ss_mask = 2;
+  optional string name = 3;
+}
+
+// End of protos/perfetto/trace/ftrace/cgroup.proto
+
 // Begin of protos/perfetto/trace/ftrace/clk.proto
 
 message ClkEnableFtraceEvent {
@@ -1005,6 +2146,96 @@
 
 // End of protos/perfetto/trace/ftrace/clk.proto
 
+// Begin of protos/perfetto/trace/ftrace/compaction.proto
+
+message MmCompactionBeginFtraceEvent {
+  optional uint64 zone_start = 1;
+  optional uint64 migrate_pfn = 2;
+  optional uint64 free_pfn = 3;
+  optional uint64 zone_end = 4;
+  optional uint32 sync = 5;
+}
+message MmCompactionDeferCompactionFtraceEvent {
+  optional int32 nid = 1;
+  optional uint32 idx = 2;
+  optional int32 order = 3;
+  optional uint32 considered = 4;
+  optional uint32 defer_shift = 5;
+  optional int32 order_failed = 6;
+}
+message MmCompactionDeferredFtraceEvent {
+  optional int32 nid = 1;
+  optional uint32 idx = 2;
+  optional int32 order = 3;
+  optional uint32 considered = 4;
+  optional uint32 defer_shift = 5;
+  optional int32 order_failed = 6;
+}
+message MmCompactionDeferResetFtraceEvent {
+  optional int32 nid = 1;
+  optional uint32 idx = 2;
+  optional int32 order = 3;
+  optional uint32 considered = 4;
+  optional uint32 defer_shift = 5;
+  optional int32 order_failed = 6;
+}
+message MmCompactionEndFtraceEvent {
+  optional uint64 zone_start = 1;
+  optional uint64 migrate_pfn = 2;
+  optional uint64 free_pfn = 3;
+  optional uint64 zone_end = 4;
+  optional uint32 sync = 5;
+  optional int32 status = 6;
+}
+message MmCompactionFinishedFtraceEvent {
+  optional int32 nid = 1;
+  optional uint32 idx = 2;
+  optional int32 order = 3;
+  optional int32 ret = 4;
+}
+message MmCompactionIsolateFreepagesFtraceEvent {
+  optional uint64 start_pfn = 1;
+  optional uint64 end_pfn = 2;
+  optional uint64 nr_scanned = 3;
+  optional uint64 nr_taken = 4;
+}
+message MmCompactionIsolateMigratepagesFtraceEvent {
+  optional uint64 start_pfn = 1;
+  optional uint64 end_pfn = 2;
+  optional uint64 nr_scanned = 3;
+  optional uint64 nr_taken = 4;
+}
+message MmCompactionKcompactdSleepFtraceEvent {
+  optional int32 nid = 1;
+}
+message MmCompactionKcompactdWakeFtraceEvent {
+  optional int32 nid = 1;
+  optional int32 order = 2;
+  optional uint32 classzone_idx = 3;
+}
+message MmCompactionMigratepagesFtraceEvent {
+  optional uint64 nr_migrated = 1;
+  optional uint64 nr_failed = 2;
+}
+message MmCompactionSuitableFtraceEvent {
+  optional int32 nid = 1;
+  optional uint32 idx = 2;
+  optional int32 order = 3;
+  optional int32 ret = 4;
+}
+message MmCompactionTryToCompactPagesFtraceEvent {
+  optional int32 order = 1;
+  optional uint32 gfp_mask = 2;
+  optional uint32 mode = 3;
+}
+message MmCompactionWakeupKcompactdFtraceEvent {
+  optional int32 nid = 1;
+  optional int32 order = 2;
+  optional uint32 classzone_idx = 3;
+}
+
+// End of protos/perfetto/trace/ftrace/compaction.proto
+
 // Begin of protos/perfetto/trace/ftrace/ext4.proto
 
 message Ext4DaWriteBeginFtraceEvent {
@@ -1911,6 +3142,35 @@
 
 // End of protos/perfetto/trace/ftrace/f2fs.proto
 
+// Begin of protos/perfetto/trace/ftrace/fence.proto
+
+message FenceInitFtraceEvent {
+  optional uint32 context = 1;
+  optional string driver = 2;
+  optional uint32 seqno = 3;
+  optional string timeline = 4;
+}
+message FenceDestroyFtraceEvent {
+  optional uint32 context = 1;
+  optional string driver = 2;
+  optional uint32 seqno = 3;
+  optional string timeline = 4;
+}
+message FenceEnableSignalFtraceEvent {
+  optional uint32 context = 1;
+  optional string driver = 2;
+  optional uint32 seqno = 3;
+  optional string timeline = 4;
+}
+message FenceSignaledFtraceEvent {
+  optional uint32 context = 1;
+  optional string driver = 2;
+  optional uint32 seqno = 3;
+  optional string timeline = 4;
+}
+
+// End of protos/perfetto/trace/ftrace/fence.proto
+
 // Begin of protos/perfetto/trace/ftrace/filemap.proto
 
 message MmFilemapAddToPageCacheFtraceEvent {
@@ -1939,474 +3199,6 @@
 
 // End of protos/perfetto/trace/ftrace/ftrace.proto
 
-// Begin of protos/perfetto/trace/ftrace/ftrace_event.proto
-
-message FtraceEvent {
-  // Nanoseconds since an epoch.
-  // Epoch is configurable by writing into trace_clock.
-  // By default this timestamp is CPU local.
-  // TODO: Figure out a story for reconciling the various clocks.
-  optional uint64 timestamp = 1;
-
-  // Kernel pid (do not confuse with userspace pid aka tgid)
-  optional uint32 pid = 2;
-
-  oneof event {
-    PrintFtraceEvent print = 3;
-    SchedSwitchFtraceEvent sched_switch = 4;
-    // removed field with id 5;
-    // removed field with id 6;
-    // removed field with id 7;
-    // removed field with id 8;
-    // removed field with id 9;
-    // removed field with id 10;
-    CpuFrequencyFtraceEvent cpu_frequency = 11;
-    CpuFrequencyLimitsFtraceEvent cpu_frequency_limits = 12;
-    CpuIdleFtraceEvent cpu_idle = 13;
-    ClockEnableFtraceEvent clock_enable = 14;
-    ClockDisableFtraceEvent clock_disable = 15;
-    ClockSetRateFtraceEvent clock_set_rate = 16;
-    SchedWakeupFtraceEvent sched_wakeup = 17;
-    SchedBlockedReasonFtraceEvent sched_blocked_reason = 18;
-    SchedCpuHotplugFtraceEvent sched_cpu_hotplug = 19;
-    SchedWakingFtraceEvent sched_waking = 20;
-    // removed field with id 21
-    // removed field with id 22
-    // removed field with id 23
-    // removed field with id 24
-    // removed field with id 25
-    // removed field with id 26
-    // removed field with id 27
-    // removed field with id 28
-    // removed field with id 29
-    // removed field with id 30
-    // removed field with id 31
-    // removed field with id 32
-    // removed field with id 33
-    // removed field with id 34
-    LowmemoryKillFtraceEvent lowmemory_kill = 35;
-    // removed field with id 36
-    // removed field with id 37
-    // removed field with id 38
-    // removed field with id 39
-    // removed field with id 40
-    Ext4DaWriteBeginFtraceEvent ext4_da_write_begin = 41;
-    Ext4DaWriteEndFtraceEvent ext4_da_write_end = 42;
-    Ext4SyncFileEnterFtraceEvent ext4_sync_file_enter = 43;
-    Ext4SyncFileExitFtraceEvent ext4_sync_file_exit = 44;
-    BlockRqIssueFtraceEvent block_rq_issue = 45;
-    MmVmscanDirectReclaimBeginFtraceEvent mm_vmscan_direct_reclaim_begin = 46;
-    MmVmscanDirectReclaimEndFtraceEvent mm_vmscan_direct_reclaim_end = 47;
-    MmVmscanKswapdWakeFtraceEvent mm_vmscan_kswapd_wake = 48;
-    MmVmscanKswapdSleepFtraceEvent mm_vmscan_kswapd_sleep = 49;
-    BinderTransactionFtraceEvent binder_transaction = 50;
-    BinderTransactionReceivedFtraceEvent binder_transaction_received = 51;
-    BinderSetPriorityFtraceEvent binder_set_priority = 52;
-    BinderLockFtraceEvent binder_lock = 53;
-    BinderLockedFtraceEvent binder_locked = 54;
-    BinderUnlockFtraceEvent binder_unlock = 55;
-    // removed field with id 56
-    // removed field with id 57
-    // removed field with id 58
-    // removed field with id 59
-    // removed field with id 60
-    // removed field with id 61
-    // removed field with id 62
-    // removed field with id 63
-    // removed field with id 64
-    // removed field with id 65
-    // removed field with id 66
-    // removed field with id 67
-    // removed field with id 68
-    // removed field with id 69
-    // removed field with id 70
-    // removed field with id 71
-    // removed field with id 72
-    // removed field with id 73
-    // removed field with id 74
-    // removed field with id 75
-    // removed field with id 76
-    // removed field with id 77
-    // removed field with id 78
-    // removed field with id 79
-    // removed field with id 80
-    // removed field with id 81
-    // removed field with id 82
-    // removed field with id 83
-    // removed field with id 84
-    // removed field with id 85
-    // removed field with id 86
-    // removed field with id 87
-    // removed field with id 88
-    // removed field with id 89
-    // removed field with id 90
-    // removed field with id 91
-    // removed field with id 92
-    // removed field with id 93
-    // removed field with id 94
-    // removed field with id 95
-    // removed field with id 96
-    MmFilemapAddToPageCacheFtraceEvent mm_filemap_add_to_page_cache = 97;
-    MmFilemapDeleteFromPageCacheFtraceEvent mm_filemap_delete_from_page_cache =
-        98;
-    // removed field with id 99
-    // removed field with id 100
-    // removed field with id 101
-    // removed field with id 102
-    // removed field with id 103
-    // removed field with id 104
-    // removed field with id 105
-    // removed field with id 106
-    // removed field with id 107
-    // removed field with id 108
-    // removed field with id 109
-    // removed field with id 110
-    // removed field with id 111
-    // removed field with id 112
-    SuspendResumeFtraceEvent suspend_resume = 113;
-    SchedWakeupNewFtraceEvent sched_wakeup_new = 114;
-    BlockBioBackmergeFtraceEvent block_bio_backmerge = 115;
-    BlockBioBounceFtraceEvent block_bio_bounce = 116;
-    BlockBioCompleteFtraceEvent block_bio_complete = 117;
-    BlockBioFrontmergeFtraceEvent block_bio_frontmerge = 118;
-    BlockBioQueueFtraceEvent block_bio_queue = 119;
-    BlockBioRemapFtraceEvent block_bio_remap = 120;
-    BlockDirtyBufferFtraceEvent block_dirty_buffer = 121;
-    BlockGetrqFtraceEvent block_getrq = 122;
-    BlockPlugFtraceEvent block_plug = 123;
-    BlockRqAbortFtraceEvent block_rq_abort = 124;
-    BlockRqCompleteFtraceEvent block_rq_complete = 125;
-    BlockRqInsertFtraceEvent block_rq_insert = 126;
-    // removed field with id 127;
-    BlockRqRemapFtraceEvent block_rq_remap = 128;
-    BlockRqRequeueFtraceEvent block_rq_requeue = 129;
-    BlockSleeprqFtraceEvent block_sleeprq = 130;
-    BlockSplitFtraceEvent block_split = 131;
-    BlockTouchBufferFtraceEvent block_touch_buffer = 132;
-    BlockUnplugFtraceEvent block_unplug = 133;
-    Ext4AllocDaBlocksFtraceEvent ext4_alloc_da_blocks = 134;
-    Ext4AllocateBlocksFtraceEvent ext4_allocate_blocks = 135;
-    Ext4AllocateInodeFtraceEvent ext4_allocate_inode = 136;
-    Ext4BeginOrderedTruncateFtraceEvent ext4_begin_ordered_truncate = 137;
-    Ext4CollapseRangeFtraceEvent ext4_collapse_range = 138;
-    Ext4DaReleaseSpaceFtraceEvent ext4_da_release_space = 139;
-    Ext4DaReserveSpaceFtraceEvent ext4_da_reserve_space = 140;
-    Ext4DaUpdateReserveSpaceFtraceEvent ext4_da_update_reserve_space = 141;
-    Ext4DaWritePagesFtraceEvent ext4_da_write_pages = 142;
-    Ext4DaWritePagesExtentFtraceEvent ext4_da_write_pages_extent = 143;
-    Ext4DirectIOEnterFtraceEvent ext4_direct_IO_enter = 144;
-    Ext4DirectIOExitFtraceEvent ext4_direct_IO_exit = 145;
-    Ext4DiscardBlocksFtraceEvent ext4_discard_blocks = 146;
-    Ext4DiscardPreallocationsFtraceEvent ext4_discard_preallocations = 147;
-    Ext4DropInodeFtraceEvent ext4_drop_inode = 148;
-    Ext4EsCacheExtentFtraceEvent ext4_es_cache_extent = 149;
-    Ext4EsFindDelayedExtentRangeEnterFtraceEvent
-        ext4_es_find_delayed_extent_range_enter = 150;
-    Ext4EsFindDelayedExtentRangeExitFtraceEvent
-        ext4_es_find_delayed_extent_range_exit = 151;
-    Ext4EsInsertExtentFtraceEvent ext4_es_insert_extent = 152;
-    Ext4EsLookupExtentEnterFtraceEvent ext4_es_lookup_extent_enter = 153;
-    Ext4EsLookupExtentExitFtraceEvent ext4_es_lookup_extent_exit = 154;
-    Ext4EsRemoveExtentFtraceEvent ext4_es_remove_extent = 155;
-    Ext4EsShrinkFtraceEvent ext4_es_shrink = 156;
-    Ext4EsShrinkCountFtraceEvent ext4_es_shrink_count = 157;
-    Ext4EsShrinkScanEnterFtraceEvent ext4_es_shrink_scan_enter = 158;
-    Ext4EsShrinkScanExitFtraceEvent ext4_es_shrink_scan_exit = 159;
-    Ext4EvictInodeFtraceEvent ext4_evict_inode = 160;
-    Ext4ExtConvertToInitializedEnterFtraceEvent
-        ext4_ext_convert_to_initialized_enter = 161;
-    Ext4ExtConvertToInitializedFastpathFtraceEvent
-        ext4_ext_convert_to_initialized_fastpath = 162;
-    Ext4ExtHandleUnwrittenExtentsFtraceEvent ext4_ext_handle_unwritten_extents =
-        163;
-    Ext4ExtInCacheFtraceEvent ext4_ext_in_cache = 164;
-    Ext4ExtLoadExtentFtraceEvent ext4_ext_load_extent = 165;
-    Ext4ExtMapBlocksEnterFtraceEvent ext4_ext_map_blocks_enter = 166;
-    Ext4ExtMapBlocksExitFtraceEvent ext4_ext_map_blocks_exit = 167;
-    Ext4ExtPutInCacheFtraceEvent ext4_ext_put_in_cache = 168;
-    Ext4ExtRemoveSpaceFtraceEvent ext4_ext_remove_space = 169;
-    Ext4ExtRemoveSpaceDoneFtraceEvent ext4_ext_remove_space_done = 170;
-    Ext4ExtRmIdxFtraceEvent ext4_ext_rm_idx = 171;
-    Ext4ExtRmLeafFtraceEvent ext4_ext_rm_leaf = 172;
-    Ext4ExtShowExtentFtraceEvent ext4_ext_show_extent = 173;
-    Ext4FallocateEnterFtraceEvent ext4_fallocate_enter = 174;
-    Ext4FallocateExitFtraceEvent ext4_fallocate_exit = 175;
-    Ext4FindDelallocRangeFtraceEvent ext4_find_delalloc_range = 176;
-    Ext4ForgetFtraceEvent ext4_forget = 177;
-    Ext4FreeBlocksFtraceEvent ext4_free_blocks = 178;
-    Ext4FreeInodeFtraceEvent ext4_free_inode = 179;
-    Ext4GetImpliedClusterAllocExitFtraceEvent
-        ext4_get_implied_cluster_alloc_exit = 180;
-    Ext4GetReservedClusterAllocFtraceEvent ext4_get_reserved_cluster_alloc =
-        181;
-    Ext4IndMapBlocksEnterFtraceEvent ext4_ind_map_blocks_enter = 182;
-    Ext4IndMapBlocksExitFtraceEvent ext4_ind_map_blocks_exit = 183;
-    Ext4InsertRangeFtraceEvent ext4_insert_range = 184;
-    Ext4InvalidatepageFtraceEvent ext4_invalidatepage = 185;
-    Ext4JournalStartFtraceEvent ext4_journal_start = 186;
-    Ext4JournalStartReservedFtraceEvent ext4_journal_start_reserved = 187;
-    Ext4JournalledInvalidatepageFtraceEvent ext4_journalled_invalidatepage =
-        188;
-    Ext4JournalledWriteEndFtraceEvent ext4_journalled_write_end = 189;
-    Ext4LoadInodeFtraceEvent ext4_load_inode = 190;
-    Ext4LoadInodeBitmapFtraceEvent ext4_load_inode_bitmap = 191;
-    Ext4MarkInodeDirtyFtraceEvent ext4_mark_inode_dirty = 192;
-    Ext4MbBitmapLoadFtraceEvent ext4_mb_bitmap_load = 193;
-    Ext4MbBuddyBitmapLoadFtraceEvent ext4_mb_buddy_bitmap_load = 194;
-    Ext4MbDiscardPreallocationsFtraceEvent ext4_mb_discard_preallocations = 195;
-    Ext4MbNewGroupPaFtraceEvent ext4_mb_new_group_pa = 196;
-    Ext4MbNewInodePaFtraceEvent ext4_mb_new_inode_pa = 197;
-    Ext4MbReleaseGroupPaFtraceEvent ext4_mb_release_group_pa = 198;
-    Ext4MbReleaseInodePaFtraceEvent ext4_mb_release_inode_pa = 199;
-    Ext4MballocAllocFtraceEvent ext4_mballoc_alloc = 200;
-    Ext4MballocDiscardFtraceEvent ext4_mballoc_discard = 201;
-    Ext4MballocFreeFtraceEvent ext4_mballoc_free = 202;
-    Ext4MballocPreallocFtraceEvent ext4_mballoc_prealloc = 203;
-    Ext4OtherInodeUpdateTimeFtraceEvent ext4_other_inode_update_time = 204;
-    Ext4PunchHoleFtraceEvent ext4_punch_hole = 205;
-    Ext4ReadBlockBitmapLoadFtraceEvent ext4_read_block_bitmap_load = 206;
-    Ext4ReadpageFtraceEvent ext4_readpage = 207;
-    Ext4ReleasepageFtraceEvent ext4_releasepage = 208;
-    Ext4RemoveBlocksFtraceEvent ext4_remove_blocks = 209;
-    Ext4RequestBlocksFtraceEvent ext4_request_blocks = 210;
-    Ext4RequestInodeFtraceEvent ext4_request_inode = 211;
-    Ext4SyncFsFtraceEvent ext4_sync_fs = 212;
-    Ext4TrimAllFreeFtraceEvent ext4_trim_all_free = 213;
-    Ext4TrimExtentFtraceEvent ext4_trim_extent = 214;
-    Ext4TruncateEnterFtraceEvent ext4_truncate_enter = 215;
-    Ext4TruncateExitFtraceEvent ext4_truncate_exit = 216;
-    Ext4UnlinkEnterFtraceEvent ext4_unlink_enter = 217;
-    Ext4UnlinkExitFtraceEvent ext4_unlink_exit = 218;
-    Ext4WriteBeginFtraceEvent ext4_write_begin = 219;
-    // removed field with id 220;
-    // removed field with id 221;
-    // removed field with id 222;
-    // removed field with id 223;
-    // removed field with id 224;
-    // removed field with id 225;
-    // removed field with id 226;
-    // removed field with id 227;
-    // removed field with id 228;
-    // removed field with id 229;
-    Ext4WriteEndFtraceEvent ext4_write_end = 230;
-    Ext4WritepageFtraceEvent ext4_writepage = 231;
-    Ext4WritepagesFtraceEvent ext4_writepages = 232;
-    Ext4WritepagesResultFtraceEvent ext4_writepages_result = 233;
-    Ext4ZeroRangeFtraceEvent ext4_zero_range = 234;
-    TaskNewtaskFtraceEvent task_newtask = 235;
-    TaskRenameFtraceEvent task_rename = 236;
-    SchedProcessExecFtraceEvent sched_process_exec = 237;
-    SchedProcessExitFtraceEvent sched_process_exit = 238;
-    SchedProcessForkFtraceEvent sched_process_fork = 239;
-    SchedProcessFreeFtraceEvent sched_process_free = 240;
-    SchedProcessHangFtraceEvent sched_process_hang = 241;
-    SchedProcessWaitFtraceEvent sched_process_wait = 242;
-    F2fsDoSubmitBioFtraceEvent f2fs_do_submit_bio = 243;
-    F2fsEvictInodeFtraceEvent f2fs_evict_inode = 244;
-    F2fsFallocateFtraceEvent f2fs_fallocate = 245;
-    F2fsGetDataBlockFtraceEvent f2fs_get_data_block = 246;
-    F2fsGetVictimFtraceEvent f2fs_get_victim = 247;
-    F2fsIgetFtraceEvent f2fs_iget = 248;
-    F2fsIgetExitFtraceEvent f2fs_iget_exit = 249;
-    F2fsNewInodeFtraceEvent f2fs_new_inode = 250;
-    F2fsReadpageFtraceEvent f2fs_readpage = 251;
-    F2fsReserveNewBlockFtraceEvent f2fs_reserve_new_block = 252;
-    F2fsSetPageDirtyFtraceEvent f2fs_set_page_dirty = 253;
-    F2fsSubmitWritePageFtraceEvent f2fs_submit_write_page = 254;
-    F2fsSyncFileEnterFtraceEvent f2fs_sync_file_enter = 255;
-    F2fsSyncFileExitFtraceEvent f2fs_sync_file_exit = 256;
-    F2fsSyncFsFtraceEvent f2fs_sync_fs = 257;
-    F2fsTruncateFtraceEvent f2fs_truncate = 258;
-    F2fsTruncateBlocksEnterFtraceEvent f2fs_truncate_blocks_enter = 259;
-    F2fsTruncateBlocksExitFtraceEvent f2fs_truncate_blocks_exit = 260;
-    F2fsTruncateDataBlocksRangeFtraceEvent f2fs_truncate_data_blocks_range =
-        261;
-    F2fsTruncateInodeBlocksEnterFtraceEvent f2fs_truncate_inode_blocks_enter =
-        262;
-    F2fsTruncateInodeBlocksExitFtraceEvent f2fs_truncate_inode_blocks_exit =
-        263;
-    F2fsTruncateNodeFtraceEvent f2fs_truncate_node = 264;
-    F2fsTruncateNodesEnterFtraceEvent f2fs_truncate_nodes_enter = 265;
-    F2fsTruncateNodesExitFtraceEvent f2fs_truncate_nodes_exit = 266;
-    F2fsTruncatePartialNodesFtraceEvent f2fs_truncate_partial_nodes = 267;
-    F2fsUnlinkEnterFtraceEvent f2fs_unlink_enter = 268;
-    F2fsUnlinkExitFtraceEvent f2fs_unlink_exit = 269;
-    F2fsVmPageMkwriteFtraceEvent f2fs_vm_page_mkwrite = 270;
-    F2fsWriteBeginFtraceEvent f2fs_write_begin = 271;
-    F2fsWriteCheckpointFtraceEvent f2fs_write_checkpoint = 272;
-    F2fsWriteEndFtraceEvent f2fs_write_end = 273;
-    AllocPagesIommuEndFtraceEvent alloc_pages_iommu_end = 274;
-    AllocPagesIommuFailFtraceEvent alloc_pages_iommu_fail = 275;
-    AllocPagesIommuStartFtraceEvent alloc_pages_iommu_start = 276;
-    AllocPagesSysEndFtraceEvent alloc_pages_sys_end = 277;
-    AllocPagesSysFailFtraceEvent alloc_pages_sys_fail = 278;
-    AllocPagesSysStartFtraceEvent alloc_pages_sys_start = 279;
-    DmaAllocContiguousRetryFtraceEvent dma_alloc_contiguous_retry = 280;
-    IommuMapRangeFtraceEvent iommu_map_range = 281;
-    IommuSecPtblMapRangeEndFtraceEvent iommu_sec_ptbl_map_range_end = 282;
-    IommuSecPtblMapRangeStartFtraceEvent iommu_sec_ptbl_map_range_start = 283;
-    IonAllocBufferEndFtraceEvent ion_alloc_buffer_end = 284;
-    IonAllocBufferFailFtraceEvent ion_alloc_buffer_fail = 285;
-    IonAllocBufferFallbackFtraceEvent ion_alloc_buffer_fallback = 286;
-    IonAllocBufferStartFtraceEvent ion_alloc_buffer_start = 287;
-    IonCpAllocRetryFtraceEvent ion_cp_alloc_retry = 288;
-    IonCpSecureBufferEndFtraceEvent ion_cp_secure_buffer_end = 289;
-    IonCpSecureBufferStartFtraceEvent ion_cp_secure_buffer_start = 290;
-    IonPrefetchingFtraceEvent ion_prefetching = 291;
-    IonSecureCmaAddToPoolEndFtraceEvent ion_secure_cma_add_to_pool_end = 292;
-    IonSecureCmaAddToPoolStartFtraceEvent ion_secure_cma_add_to_pool_start =
-        293;
-    IonSecureCmaAllocateEndFtraceEvent ion_secure_cma_allocate_end = 294;
-    IonSecureCmaAllocateStartFtraceEvent ion_secure_cma_allocate_start = 295;
-    IonSecureCmaShrinkPoolEndFtraceEvent ion_secure_cma_shrink_pool_end = 296;
-    IonSecureCmaShrinkPoolStartFtraceEvent ion_secure_cma_shrink_pool_start =
-        297;
-    KfreeFtraceEvent kfree = 298;
-    KmallocFtraceEvent kmalloc = 299;
-    KmallocNodeFtraceEvent kmalloc_node = 300;
-    KmemCacheAllocFtraceEvent kmem_cache_alloc = 301;
-    KmemCacheAllocNodeFtraceEvent kmem_cache_alloc_node = 302;
-    KmemCacheFreeFtraceEvent kmem_cache_free = 303;
-    MigratePagesEndFtraceEvent migrate_pages_end = 304;
-    MigratePagesStartFtraceEvent migrate_pages_start = 305;
-    MigrateRetryFtraceEvent migrate_retry = 306;
-    MmPageAllocFtraceEvent mm_page_alloc = 307;
-    MmPageAllocExtfragFtraceEvent mm_page_alloc_extfrag = 308;
-    MmPageAllocZoneLockedFtraceEvent mm_page_alloc_zone_locked = 309;
-    MmPageFreeFtraceEvent mm_page_free = 310;
-    MmPageFreeBatchedFtraceEvent mm_page_free_batched = 311;
-    MmPagePcpuDrainFtraceEvent mm_page_pcpu_drain = 312;
-    RssStatFtraceEvent rss_stat = 313;
-    IonHeapShrinkFtraceEvent ion_heap_shrink = 314;
-    IonHeapGrowFtraceEvent ion_heap_grow = 315;
-    // removed field with id 316
-    // removed field with id 317
-    // removed field with id 318
-    // removed field with id 319
-    ClkEnableFtraceEvent clk_enable = 320;
-    ClkDisableFtraceEvent clk_disable = 321;
-    ClkSetRateFtraceEvent clk_set_rate = 322;
-    BinderTransactionAllocBufFtraceEvent binder_transaction_alloc_buf = 323;
-    SignalDeliverFtraceEvent signal_deliver = 324;
-    SignalGenerateFtraceEvent signal_generate = 325;
-    // removed field with id 326
-    GenericFtraceEvent generic = 327;
-    MmEventRecordFtraceEvent mm_event_record = 328;
-    SysEnterFtraceEvent sys_enter = 329;
-    SysExitFtraceEvent sys_exit = 330;
-    ZeroFtraceEvent zero = 331;
-    GpuFrequencyFtraceEvent gpu_frequency = 332;
-    // removed field with id 333
-  }
-}
-
-// End of protos/perfetto/trace/ftrace/ftrace_event.proto
-
-// Begin of protos/perfetto/trace/ftrace/ftrace_event_bundle.proto
-
-// The result of tracing one or more ftrace data pages from a single per-cpu
-// kernel ring buffer. If collating multiple pages' worth of events, all of
-// them come from contiguous pages, with no kernel data loss in between.
-message FtraceEventBundle {
-  optional uint32 cpu = 1;
-  repeated FtraceEvent event = 2;
-  // Set to true if there was data loss between the last time we've read from
-  // the corresponding per-cpu kernel buffer, and the earliest event recorded
-  // in this bundle.
-  optional bool lost_events = 3;
-
-  // Optionally-enabled compact encoding of a batch of scheduling events. Only
-  // a subset of events & their fields is recorded.
-  // All fields (except comms) are stored in a structure-of-arrays form, one
-  // entry in each repeated field per event.
-  message CompactSched {
-    // Interned table of unique strings for this bundle.
-    repeated string intern_table = 5;
-
-    // Delta-encoded timestamps across all sched_switch events within this
-    // bundle. The first is absolute, each next one is relative to its
-    // predecessor.
-    repeated uint64 switch_timestamp = 1 [packed = true];
-    repeated int64 switch_prev_state = 2 [packed = true];
-    repeated int32 switch_next_pid = 3 [packed = true];
-    repeated int32 switch_next_prio = 4 [packed = true];
-    // One per event, index into |intern_table| corresponding to the
-    // next_comm field of the event.
-    repeated uint32 switch_next_comm_index = 6 [packed = true];
-
-    // Delta-encoded timestamps across all sched_waking events within this
-    // bundle. The first is absolute, each next one is relative to its
-    // predecessor.
-    repeated uint64 waking_timestamp = 7 [packed = true];
-    repeated int32 waking_pid = 8 [packed = true];
-    repeated int32 waking_target_cpu = 9 [packed = true];
-    repeated int32 waking_prio = 10 [packed = true];
-    // One per event, index into |intern_table| corresponding to the
-    // comm field of the event.
-    repeated uint32 waking_comm_index = 11 [packed = true];
-  }
-  optional CompactSched compact_sched = 4;
-}
-
-// End of protos/perfetto/trace/ftrace/ftrace_event_bundle.proto
-
-// Begin of protos/perfetto/trace/ftrace/ftrace_stats.proto
-
-// Per-CPU stats for the ftrace data source gathered from the kernel from
-// /sys/kernel/debug/tracing/per_cpu/cpuX/stats.
-message FtraceCpuStats {
-  // CPU index.
-  optional uint64 cpu = 1;
-
-  // Number of entries still in the kernel buffer. Ideally this should be close
-  // to zero, as events are consumed regularly and moved into the userspace
-  // buffers (or file).
-  optional uint64 entries = 2;
-
-  // Number of events lost in kernel buffers due to overwriting of old events
-  // before userspace had a chance to drain them.
-  optional uint64 overrun = 3;
-
-  // This should always be zero. If not the buffer size is way too small or
-  // something went wrong with the tracer.
-  optional uint64 commit_overrun = 4;
-
-  // Bytes actually read (not overwritten).
-  optional uint64 bytes_read = 5;
-
-  // The timestamp for the oldest event still in the ring buffer.
-  optional double oldest_event_ts = 6;
-
-  // The current timestamp.
-  optional double now_ts = 7;
-
-  // If the kernel buffer has overwrite mode disabled, this will show the number
-  // of new events that were lost because the buffer was full. This is similar
-  // to |overrun| but only for the overwrite=false case.
-  optional uint64 dropped_events = 8;
-
-  // The number of events read.
-  optional uint64 read_events = 9;
-}
-
-// Ftrace stats for all CPUs.
-message FtraceStats {
-  enum Phase {
-    UNSPECIFIED = 0;
-    START_OF_TRACE = 1;
-    END_OF_TRACE = 2;
-  }
-
-  // Tells when stats were sampled. There should be one sample at the beginning
-  // of the trace and one sample at the end.
-  optional Phase phase = 1;
-
-  // Per-CPU stats (one entry for each CPU).
-  repeated FtraceCpuStats cpu_stats = 2;
-}
-
-// End of protos/perfetto/trace/ftrace/ftrace_stats.proto
-
 // Begin of protos/perfetto/trace/ftrace/generic.proto
 
 // This generic proto is used to output events in the trace
@@ -2427,6 +3219,119 @@
 
 // End of protos/perfetto/trace/ftrace/generic.proto
 
+// Begin of protos/perfetto/trace/ftrace/i2c.proto
+
+message I2cReadFtraceEvent {
+  optional int32 adapter_nr = 1;
+  optional uint32 msg_nr = 2;
+  optional uint32 addr = 3;
+  optional uint32 flags = 4;
+  optional uint32 len = 5;
+}
+message I2cWriteFtraceEvent {
+  optional int32 adapter_nr = 1;
+  optional uint32 msg_nr = 2;
+  optional uint32 addr = 3;
+  optional uint32 flags = 4;
+  optional uint32 len = 5;
+  optional uint32 buf = 6;
+}
+message I2cResultFtraceEvent {
+  optional int32 adapter_nr = 1;
+  optional uint32 nr_msgs = 2;
+  optional int32 ret = 3;
+}
+message I2cReplyFtraceEvent {
+  optional int32 adapter_nr = 1;
+  optional uint32 msg_nr = 2;
+  optional uint32 addr = 3;
+  optional uint32 flags = 4;
+  optional uint32 len = 5;
+  optional uint32 buf = 6;
+}
+message SmbusReadFtraceEvent {
+  optional int32 adapter_nr = 1;
+  optional uint32 flags = 2;
+  optional uint32 addr = 3;
+  optional uint32 command = 4;
+  optional uint32 protocol = 5;
+}
+message SmbusWriteFtraceEvent {
+  optional int32 adapter_nr = 1;
+  optional uint32 addr = 2;
+  optional uint32 flags = 3;
+  optional uint32 command = 4;
+  optional uint32 len = 5;
+  optional uint32 protocol = 6;
+}
+message SmbusResultFtraceEvent {
+  optional int32 adapter_nr = 1;
+  optional uint32 addr = 2;
+  optional uint32 flags = 3;
+  optional uint32 read_write = 4;
+  optional uint32 command = 5;
+  optional int32 res = 6;
+  optional uint32 protocol = 7;
+}
+message SmbusReplyFtraceEvent {
+  optional int32 adapter_nr = 1;
+  optional uint32 addr = 2;
+  optional uint32 flags = 3;
+  optional uint32 command = 4;
+  optional uint32 len = 5;
+  optional uint32 protocol = 6;
+}
+
+// End of protos/perfetto/trace/ftrace/i2c.proto
+
+// Begin of protos/perfetto/trace/ftrace/ion.proto
+
+message IonStatFtraceEvent {
+  optional uint32 buffer_id = 1;
+  optional int64 len = 2;
+  optional uint64 total_allocated = 3;
+}
+
+// End of protos/perfetto/trace/ftrace/ion.proto
+
+// Begin of protos/perfetto/trace/ftrace/ipi.proto
+
+message IpiEntryFtraceEvent {
+  optional string reason = 1;
+}
+message IpiExitFtraceEvent {
+  optional string reason = 1;
+}
+message IpiRaiseFtraceEvent {
+  optional uint32 target_cpus = 1;
+  optional string reason = 2;
+}
+
+// End of protos/perfetto/trace/ftrace/ipi.proto
+
+// Begin of protos/perfetto/trace/ftrace/irq.proto
+
+message SoftirqEntryFtraceEvent {
+  optional uint32 vec = 1;
+}
+message SoftirqExitFtraceEvent {
+  optional uint32 vec = 1;
+}
+message SoftirqRaiseFtraceEvent {
+  optional uint32 vec = 1;
+}
+message IrqHandlerEntryFtraceEvent {
+  optional int32 irq = 1;
+  optional string name = 2;
+  optional uint32 handler = 3;
+}
+message IrqHandlerExitFtraceEvent {
+  optional int32 irq = 1;
+  optional int32 ret = 2;
+}
+
+// End of protos/perfetto/trace/ftrace/irq.proto
+
 // Begin of protos/perfetto/trace/ftrace/kmem.proto
 
 message AllocPagesIommuEndFtraceEvent {
@@ -2670,6 +3575,158 @@
 
 // End of protos/perfetto/trace/ftrace/lowmemorykiller.proto
 
+// Begin of protos/perfetto/trace/ftrace/mdss.proto
+
+message MdpCmdKickoffFtraceEvent {
+  optional uint32 ctl_num = 1;
+  optional int32 kickoff_cnt = 2;
+}
+message MdpCommitFtraceEvent {
+  optional uint32 num = 1;
+  optional uint32 play_cnt = 2;
+  optional uint32 clk_rate = 3;
+  optional uint64 bandwidth = 4;
+}
+message MdpPerfSetOtFtraceEvent {
+  optional uint32 pnum = 1;
+  optional uint32 xin_id = 2;
+  optional uint32 rd_lim = 3;
+  optional uint32 is_vbif_rt = 4;
+}
+message MdpSsppChangeFtraceEvent {
+  optional uint32 num = 1;
+  optional uint32 play_cnt = 2;
+  optional uint32 mixer = 3;
+  optional uint32 stage = 4;
+  optional uint32 flags = 5;
+  optional uint32 format = 6;
+  optional uint32 img_w = 7;
+  optional uint32 img_h = 8;
+  optional uint32 src_x = 9;
+  optional uint32 src_y = 10;
+  optional uint32 src_w = 11;
+  optional uint32 src_h = 12;
+  optional uint32 dst_x = 13;
+  optional uint32 dst_y = 14;
+  optional uint32 dst_w = 15;
+  optional uint32 dst_h = 16;
+}
+message TracingMarkWriteFtraceEvent {
+  optional int32 pid = 1;
+  optional string trace_name = 2;
+  optional uint32 trace_begin = 3;
+}
+message MdpCmdPingpongDoneFtraceEvent {
+  optional uint32 ctl_num = 1;
+  optional uint32 intf_num = 2;
+  optional uint32 pp_num = 3;
+  optional int32 koff_cnt = 4;
+}
+message MdpCompareBwFtraceEvent {
+  optional uint64 new_ab = 1;
+  optional uint64 new_ib = 2;
+  optional uint64 new_wb = 3;
+  optional uint64 old_ab = 4;
+  optional uint64 old_ib = 5;
+  optional uint64 old_wb = 6;
+  optional uint32 params_changed = 7;
+  optional uint32 update_bw = 8;
+}
+message MdpPerfSetPanicLutsFtraceEvent {
+  optional uint32 pnum = 1;
+  optional uint32 fmt = 2;
+  optional uint32 mode = 3;
+  optional uint32 panic_lut = 4;
+  optional uint32 robust_lut = 5;
+}
+message MdpSsppSetFtraceEvent {
+  optional uint32 num = 1;
+  optional uint32 play_cnt = 2;
+  optional uint32 mixer = 3;
+  optional uint32 stage = 4;
+  optional uint32 flags = 5;
+  optional uint32 format = 6;
+  optional uint32 img_w = 7;
+  optional uint32 img_h = 8;
+  optional uint32 src_x = 9;
+  optional uint32 src_y = 10;
+  optional uint32 src_w = 11;
+  optional uint32 src_h = 12;
+  optional uint32 dst_x = 13;
+  optional uint32 dst_y = 14;
+  optional uint32 dst_w = 15;
+  optional uint32 dst_h = 16;
+}
+message MdpCmdReadptrDoneFtraceEvent {
+  optional uint32 ctl_num = 1;
+  optional int32 koff_cnt = 2;
+}
+message MdpMisrCrcFtraceEvent {
+  optional uint32 block_id = 1;
+  optional uint32 vsync_cnt = 2;
+  optional uint32 crc = 3;
+}
+message MdpPerfSetQosLutsFtraceEvent {
+  optional uint32 pnum = 1;
+  optional uint32 fmt = 2;
+  optional uint32 intf = 3;
+  optional uint32 rot = 4;
+  optional uint32 fl = 5;
+  optional uint32 lut = 6;
+  optional uint32 linear = 7;
+}
+message MdpTraceCounterFtraceEvent {
+  optional int32 pid = 1;
+  optional string counter_name = 2;
+  optional int32 value = 3;
+}
+message MdpCmdReleaseBwFtraceEvent {
+  optional uint32 ctl_num = 1;
+}
+message MdpMixerUpdateFtraceEvent {
+  optional uint32 mixer_num = 1;
+}
+message MdpPerfSetWmLevelsFtraceEvent {
+  optional uint32 pnum = 1;
+  optional uint32 use_space = 2;
+  optional uint32 priority_bytes = 3;
+  optional uint32 wm0 = 4;
+  optional uint32 wm1 = 5;
+  optional uint32 wm2 = 6;
+  optional uint32 mb_cnt = 7;
+  optional uint32 mb_size = 8;
+}
+message MdpVideoUnderrunDoneFtraceEvent {
+  optional uint32 ctl_num = 1;
+  optional uint32 underrun_cnt = 2;
+}
+message MdpCmdWaitPingpongFtraceEvent {
+  optional uint32 ctl_num = 1;
+  optional int32 kickoff_cnt = 2;
+}
+message MdpPerfPrefillCalcFtraceEvent {
+  optional uint32 pnum = 1;
+  optional uint32 latency_buf = 2;
+  optional uint32 ot = 3;
+  optional uint32 y_buf = 4;
+  optional uint32 y_scaler = 5;
+  optional uint32 pp_lines = 6;
+  optional uint32 pp_bytes = 7;
+  optional uint32 post_sc = 8;
+  optional uint32 fbc_bytes = 9;
+  optional uint32 prefill_bytes = 10;
+}
+message MdpPerfUpdateBusFtraceEvent {
+  optional int32 client = 1;
+  optional uint64 ab_quota = 2;
+  optional uint64 ib_quota = 3;
+}
+message RotatorBwAoAsContextFtraceEvent {
+  optional uint32 state = 1;
+}
+
+// End of protos/perfetto/trace/ftrace/mdss.proto
+
 // Begin of protos/perfetto/trace/ftrace/mm_event.proto
 
 message MmEventRecordFtraceEvent {
@@ -2681,6 +3738,19 @@
 
 // End of protos/perfetto/trace/ftrace/mm_event.proto
 
+// Begin of protos/perfetto/trace/ftrace/oom.proto
+
+message OomScoreAdjUpdateFtraceEvent {
+  optional string comm = 1;
+  optional int32 oom_score_adj = 2;
+  optional int32 pid = 3;
+}
+message MarkVictimFtraceEvent {
+  optional int32 pid = 1;
+}
+
+// End of protos/perfetto/trace/ftrace/oom.proto
+
 // Begin of protos/perfetto/trace/ftrace/power.proto
 
 message CpuFrequencyFtraceEvent {
@@ -2735,6 +3805,35 @@
 
 // End of protos/perfetto/trace/ftrace/raw_syscalls.proto
 
+// Begin of protos/perfetto/trace/ftrace/regulator.proto
+
+message RegulatorDisableFtraceEvent {
+  optional string name = 1;
+}
+message RegulatorDisableCompleteFtraceEvent {
+  optional string name = 1;
+}
+message RegulatorEnableFtraceEvent {
+  optional string name = 1;
+}
+message RegulatorEnableCompleteFtraceEvent {
+  optional string name = 1;
+}
+message RegulatorEnableDelayFtraceEvent {
+  optional string name = 1;
+}
+message RegulatorSetVoltageFtraceEvent {
+  optional string name = 1;
+  optional int32 min = 2;
+  optional int32 max = 3;
+}
+message RegulatorSetVoltageCompleteFtraceEvent {
+  optional string name = 1;
+  optional uint32 val = 2;
+}
+
+// End of protos/perfetto/trace/ftrace/regulator.proto
+
 // Begin of protos/perfetto/trace/ftrace/sched.proto
 
 message SchedSwitchFtraceEvent {
@@ -2811,6 +3910,18 @@
 
 // End of protos/perfetto/trace/ftrace/sched.proto
 
+// Begin of protos/perfetto/trace/ftrace/sde.proto
+
+message SdeTracingMarkWriteFtraceEvent {
+  optional int32 pid = 1;
+  optional string trace_name = 2;
+  optional uint32 trace_type = 3;
+  optional int32 value = 4;
+  optional uint32 trace_begin = 5;
+}
+
+// End of protos/perfetto/trace/ftrace/sde.proto
+
 // Begin of protos/perfetto/trace/ftrace/signal.proto
 
 message SignalDeliverFtraceEvent {
@@ -2829,6 +3940,24 @@
 
 // End of protos/perfetto/trace/ftrace/signal.proto
 
+// Begin of protos/perfetto/trace/ftrace/sync.proto
+
+message SyncPtFtraceEvent {
+  optional string timeline = 1;
+  optional string value = 2;
+}
+message SyncTimelineFtraceEvent {
+  optional string name = 1;
+  optional string value = 2;
+}
+message SyncWaitFtraceEvent {
+  optional string name = 1;
+  optional int32 status = 2;
+  optional uint32 begin = 3;
+}
+
+// End of protos/perfetto/trace/ftrace/sync.proto
+
 // Begin of protos/perfetto/trace/ftrace/systrace.proto
 
 message ZeroFtraceEvent {
@@ -2877,249 +4006,733 @@
 
 // End of protos/perfetto/trace/ftrace/vmscan.proto
 
-// Begin of protos/perfetto/trace/interned_data/interned_data.proto
+// Begin of protos/perfetto/trace/ftrace/workqueue.proto
 
-// ------------------------------ DATA INTERNING: ------------------------------
-// Interning indexes are built up gradually by adding the entries contained in
-// each TracePacket of the same packet sequence (packets emitted by the same
-// producer and TraceWriter, see |trusted_packet_sequence_id|). Thus, packets
-// can only refer to interned data from other packets in the same sequence.
-//
-// The writer will emit new entries when it encounters new internable values
-// that aren't yet in the index. Data in current and subsequent TracePackets can
-// then refer to the entry by its position (interning ID, abbreviated "iid") in
-// its index. An interning ID with value 0 is considered invalid (not set).
-//
-// Because of the incremental build-up, the interning index will miss data when
-// TracePackets are lost, e.g. because a chunk was overridden in the central
-// ring buffer. To avoid invalidation of the whole trace in such a case, the
-// index is periodically reset (see SEQ_INCREMENTAL_STATE_CLEARED).
-// When packet loss occurs, the reader will only lose interning data up to the
-// next reset.
-// -----------------------------------------------------------------------------
-
-// Message that contains new entries for the interning indices of a packet
-// sequence.
-//
-// The writer will usually emit new entries in the same TracePacket that first
-// refers to them (since the last reset of interning state). They may also be
-// emitted proactively in advance of referring to them in later packets.
-//
-// Next reserved id: 8 (up to 15).
-// Next id: 23.
-message InternedData {
-  // TODO(eseckler): Replace iid fields inside interned messages with
-  // map<iid, message> type fields in InternedData.
-
-  // Each field's message type needs to specify an |iid| field, which is the ID
-  // of the entry in the field's interning index. Each field constructs its own
-  // index, thus interning IDs are scoped to the tracing session and field
-  // (usually as a counter for efficient var-int encoding). It is illegal to
-  // override entries in an index (using the same iid for two different values)
-  // within the same tracing session, even after a reset of the emitted
-  // interning state.
-  repeated EventCategory event_categories = 1;
-  repeated EventName event_names = 2;
-  repeated DebugAnnotationName debug_annotation_names = 3;
-  repeated SourceLocation source_locations = 4;
-  repeated LogMessageBody log_message_body = 20;
-
-  // Note: field IDs up to 15 should be used for frequent data only.
-
-  // Build IDs of exectuable files.
-  repeated InternedString build_ids = 16;
-  // Paths to executable files.
-  repeated InternedString mapping_paths = 17;
-  // Paths to source files.
-  repeated InternedString source_paths = 18;
-  // Names of functions used in frames below.
-  repeated InternedString function_names = 5;
-  // Symbols that were added to this trace after the fact.
-  repeated ProfiledFrameSymbols profiled_frame_symbols = 21;
-
-  // Executable files mapped into processes.
-  repeated Mapping mappings = 19;
-  // Frames of callstacks of a program.
-  repeated Frame frames = 6;
-  // A callstack of a program.
-  repeated Callstack callstacks = 7;
-
-  // Additional Vulkan information sent in a VulkanMemoryEvent message
-  repeated InternedString vulkan_memory_keys = 22;
+message WorkqueueActivateWorkFtraceEvent {
+  optional uint64 work = 1;
+}
+message WorkqueueExecuteEndFtraceEvent {
+  optional uint64 work = 1;
+}
+message WorkqueueExecuteStartFtraceEvent {
+  optional uint64 work = 1;
+  optional uint64 function = 2;
+}
+message WorkqueueQueueWorkFtraceEvent {
+  optional uint64 work = 1;
+  optional uint64 function = 2;
+  optional uint64 workqueue = 3;
+  optional uint32 req_cpu = 4;
+  optional uint32 cpu = 5;
 }
 
-// End of protos/perfetto/trace/interned_data/interned_data.proto
+// End of protos/perfetto/trace/ftrace/workqueue.proto
 
-// Begin of protos/perfetto/trace/perfetto/perfetto_metatrace.proto
+// Begin of protos/perfetto/trace/ftrace/ftrace_event.proto
 
-// Used to trace the execution of perfetto itself.
-message PerfettoMetatrace {
-  // See base/metatrace_events.h for definitions.
-  oneof record_type {
-    uint32 event_id = 1;
-    uint32 counter_id = 2;
+message FtraceEvent {
+  // Nanoseconds since an epoch.
+  // Epoch is configurable by writing into trace_clock.
+  // By default this timestamp is CPU local.
+  // TODO: Figure out a story for reconciling the various clocks.
+  optional uint64 timestamp = 1;
+
+  // Kernel pid (do not confuse with userspace pid aka tgid)
+  optional uint32 pid = 2;
+
+  oneof event {
+    PrintFtraceEvent print = 3;
+    SchedSwitchFtraceEvent sched_switch = 4;
+    // removed field with id 5;
+    // removed field with id 6;
+    // removed field with id 7;
+    // removed field with id 8;
+    // removed field with id 9;
+    // removed field with id 10;
+    CpuFrequencyFtraceEvent cpu_frequency = 11;
+    CpuFrequencyLimitsFtraceEvent cpu_frequency_limits = 12;
+    CpuIdleFtraceEvent cpu_idle = 13;
+    ClockEnableFtraceEvent clock_enable = 14;
+    ClockDisableFtraceEvent clock_disable = 15;
+    ClockSetRateFtraceEvent clock_set_rate = 16;
+    SchedWakeupFtraceEvent sched_wakeup = 17;
+    SchedBlockedReasonFtraceEvent sched_blocked_reason = 18;
+    SchedCpuHotplugFtraceEvent sched_cpu_hotplug = 19;
+    SchedWakingFtraceEvent sched_waking = 20;
+    IpiEntryFtraceEvent ipi_entry = 21;
+    IpiExitFtraceEvent ipi_exit = 22;
+    IpiRaiseFtraceEvent ipi_raise = 23;
+    SoftirqEntryFtraceEvent softirq_entry = 24;
+    SoftirqExitFtraceEvent softirq_exit = 25;
+    SoftirqRaiseFtraceEvent softirq_raise = 26;
+    I2cReadFtraceEvent i2c_read = 27;
+    I2cWriteFtraceEvent i2c_write = 28;
+    I2cResultFtraceEvent i2c_result = 29;
+    I2cReplyFtraceEvent i2c_reply = 30;
+    SmbusReadFtraceEvent smbus_read = 31;
+    SmbusWriteFtraceEvent smbus_write = 32;
+    SmbusResultFtraceEvent smbus_result = 33;
+    SmbusReplyFtraceEvent smbus_reply = 34;
+    LowmemoryKillFtraceEvent lowmemory_kill = 35;
+    IrqHandlerEntryFtraceEvent irq_handler_entry = 36;
+    IrqHandlerExitFtraceEvent irq_handler_exit = 37;
+    SyncPtFtraceEvent sync_pt = 38;
+    SyncTimelineFtraceEvent sync_timeline = 39;
+    SyncWaitFtraceEvent sync_wait = 40;
+    Ext4DaWriteBeginFtraceEvent ext4_da_write_begin = 41;
+    Ext4DaWriteEndFtraceEvent ext4_da_write_end = 42;
+    Ext4SyncFileEnterFtraceEvent ext4_sync_file_enter = 43;
+    Ext4SyncFileExitFtraceEvent ext4_sync_file_exit = 44;
+    BlockRqIssueFtraceEvent block_rq_issue = 45;
+    MmVmscanDirectReclaimBeginFtraceEvent mm_vmscan_direct_reclaim_begin = 46;
+    MmVmscanDirectReclaimEndFtraceEvent mm_vmscan_direct_reclaim_end = 47;
+    MmVmscanKswapdWakeFtraceEvent mm_vmscan_kswapd_wake = 48;
+    MmVmscanKswapdSleepFtraceEvent mm_vmscan_kswapd_sleep = 49;
+    BinderTransactionFtraceEvent binder_transaction = 50;
+    BinderTransactionReceivedFtraceEvent binder_transaction_received = 51;
+    BinderSetPriorityFtraceEvent binder_set_priority = 52;
+    BinderLockFtraceEvent binder_lock = 53;
+    BinderLockedFtraceEvent binder_locked = 54;
+    BinderUnlockFtraceEvent binder_unlock = 55;
+    WorkqueueActivateWorkFtraceEvent workqueue_activate_work = 56;
+    WorkqueueExecuteEndFtraceEvent workqueue_execute_end = 57;
+    WorkqueueExecuteStartFtraceEvent workqueue_execute_start = 58;
+    WorkqueueQueueWorkFtraceEvent workqueue_queue_work = 59;
+    RegulatorDisableFtraceEvent regulator_disable = 60;
+    RegulatorDisableCompleteFtraceEvent regulator_disable_complete = 61;
+    RegulatorEnableFtraceEvent regulator_enable = 62;
+    RegulatorEnableCompleteFtraceEvent regulator_enable_complete = 63;
+    RegulatorEnableDelayFtraceEvent regulator_enable_delay = 64;
+    RegulatorSetVoltageFtraceEvent regulator_set_voltage = 65;
+    RegulatorSetVoltageCompleteFtraceEvent regulator_set_voltage_complete = 66;
+    CgroupAttachTaskFtraceEvent cgroup_attach_task = 67;
+    CgroupMkdirFtraceEvent cgroup_mkdir = 68;
+    CgroupRemountFtraceEvent cgroup_remount = 69;
+    CgroupRmdirFtraceEvent cgroup_rmdir = 70;
+    CgroupTransferTasksFtraceEvent cgroup_transfer_tasks = 71;
+    CgroupDestroyRootFtraceEvent cgroup_destroy_root = 72;
+    CgroupReleaseFtraceEvent cgroup_release = 73;
+    CgroupRenameFtraceEvent cgroup_rename = 74;
+    CgroupSetupRootFtraceEvent cgroup_setup_root = 75;
+    MdpCmdKickoffFtraceEvent mdp_cmd_kickoff = 76;
+    MdpCommitFtraceEvent mdp_commit = 77;
+    MdpPerfSetOtFtraceEvent mdp_perf_set_ot = 78;
+    MdpSsppChangeFtraceEvent mdp_sspp_change = 79;
+    TracingMarkWriteFtraceEvent tracing_mark_write = 80;
+    MdpCmdPingpongDoneFtraceEvent mdp_cmd_pingpong_done = 81;
+    MdpCompareBwFtraceEvent mdp_compare_bw = 82;
+    MdpPerfSetPanicLutsFtraceEvent mdp_perf_set_panic_luts = 83;
+    MdpSsppSetFtraceEvent mdp_sspp_set = 84;
+    MdpCmdReadptrDoneFtraceEvent mdp_cmd_readptr_done = 85;
+    MdpMisrCrcFtraceEvent mdp_misr_crc = 86;
+    MdpPerfSetQosLutsFtraceEvent mdp_perf_set_qos_luts = 87;
+    MdpTraceCounterFtraceEvent mdp_trace_counter = 88;
+    MdpCmdReleaseBwFtraceEvent mdp_cmd_release_bw = 89;
+    MdpMixerUpdateFtraceEvent mdp_mixer_update = 90;
+    MdpPerfSetWmLevelsFtraceEvent mdp_perf_set_wm_levels = 91;
+    MdpVideoUnderrunDoneFtraceEvent mdp_video_underrun_done = 92;
+    MdpCmdWaitPingpongFtraceEvent mdp_cmd_wait_pingpong = 93;
+    MdpPerfPrefillCalcFtraceEvent mdp_perf_prefill_calc = 94;
+    MdpPerfUpdateBusFtraceEvent mdp_perf_update_bus = 95;
+    RotatorBwAoAsContextFtraceEvent rotator_bw_ao_as_context = 96;
+    MmFilemapAddToPageCacheFtraceEvent mm_filemap_add_to_page_cache = 97;
+    MmFilemapDeleteFromPageCacheFtraceEvent mm_filemap_delete_from_page_cache =
+        98;
+    MmCompactionBeginFtraceEvent mm_compaction_begin = 99;
+    MmCompactionDeferCompactionFtraceEvent mm_compaction_defer_compaction = 100;
+    MmCompactionDeferredFtraceEvent mm_compaction_deferred = 101;
+    MmCompactionDeferResetFtraceEvent mm_compaction_defer_reset = 102;
+    MmCompactionEndFtraceEvent mm_compaction_end = 103;
+    MmCompactionFinishedFtraceEvent mm_compaction_finished = 104;
+    MmCompactionIsolateFreepagesFtraceEvent mm_compaction_isolate_freepages =
+        105;
+    MmCompactionIsolateMigratepagesFtraceEvent
+        mm_compaction_isolate_migratepages = 106;
+    MmCompactionKcompactdSleepFtraceEvent mm_compaction_kcompactd_sleep = 107;
+    MmCompactionKcompactdWakeFtraceEvent mm_compaction_kcompactd_wake = 108;
+    MmCompactionMigratepagesFtraceEvent mm_compaction_migratepages = 109;
+    MmCompactionSuitableFtraceEvent mm_compaction_suitable = 110;
+    MmCompactionTryToCompactPagesFtraceEvent
+        mm_compaction_try_to_compact_pages = 111;
+    MmCompactionWakeupKcompactdFtraceEvent mm_compaction_wakeup_kcompactd = 112;
+    SuspendResumeFtraceEvent suspend_resume = 113;
+    SchedWakeupNewFtraceEvent sched_wakeup_new = 114;
+    BlockBioBackmergeFtraceEvent block_bio_backmerge = 115;
+    BlockBioBounceFtraceEvent block_bio_bounce = 116;
+    BlockBioCompleteFtraceEvent block_bio_complete = 117;
+    BlockBioFrontmergeFtraceEvent block_bio_frontmerge = 118;
+    BlockBioQueueFtraceEvent block_bio_queue = 119;
+    BlockBioRemapFtraceEvent block_bio_remap = 120;
+    BlockDirtyBufferFtraceEvent block_dirty_buffer = 121;
+    BlockGetrqFtraceEvent block_getrq = 122;
+    BlockPlugFtraceEvent block_plug = 123;
+    BlockRqAbortFtraceEvent block_rq_abort = 124;
+    BlockRqCompleteFtraceEvent block_rq_complete = 125;
+    BlockRqInsertFtraceEvent block_rq_insert = 126;
+    // removed field with id 127;
+    BlockRqRemapFtraceEvent block_rq_remap = 128;
+    BlockRqRequeueFtraceEvent block_rq_requeue = 129;
+    BlockSleeprqFtraceEvent block_sleeprq = 130;
+    BlockSplitFtraceEvent block_split = 131;
+    BlockTouchBufferFtraceEvent block_touch_buffer = 132;
+    BlockUnplugFtraceEvent block_unplug = 133;
+    Ext4AllocDaBlocksFtraceEvent ext4_alloc_da_blocks = 134;
+    Ext4AllocateBlocksFtraceEvent ext4_allocate_blocks = 135;
+    Ext4AllocateInodeFtraceEvent ext4_allocate_inode = 136;
+    Ext4BeginOrderedTruncateFtraceEvent ext4_begin_ordered_truncate = 137;
+    Ext4CollapseRangeFtraceEvent ext4_collapse_range = 138;
+    Ext4DaReleaseSpaceFtraceEvent ext4_da_release_space = 139;
+    Ext4DaReserveSpaceFtraceEvent ext4_da_reserve_space = 140;
+    Ext4DaUpdateReserveSpaceFtraceEvent ext4_da_update_reserve_space = 141;
+    Ext4DaWritePagesFtraceEvent ext4_da_write_pages = 142;
+    Ext4DaWritePagesExtentFtraceEvent ext4_da_write_pages_extent = 143;
+    Ext4DirectIOEnterFtraceEvent ext4_direct_IO_enter = 144;
+    Ext4DirectIOExitFtraceEvent ext4_direct_IO_exit = 145;
+    Ext4DiscardBlocksFtraceEvent ext4_discard_blocks = 146;
+    Ext4DiscardPreallocationsFtraceEvent ext4_discard_preallocations = 147;
+    Ext4DropInodeFtraceEvent ext4_drop_inode = 148;
+    Ext4EsCacheExtentFtraceEvent ext4_es_cache_extent = 149;
+    Ext4EsFindDelayedExtentRangeEnterFtraceEvent
+        ext4_es_find_delayed_extent_range_enter = 150;
+    Ext4EsFindDelayedExtentRangeExitFtraceEvent
+        ext4_es_find_delayed_extent_range_exit = 151;
+    Ext4EsInsertExtentFtraceEvent ext4_es_insert_extent = 152;
+    Ext4EsLookupExtentEnterFtraceEvent ext4_es_lookup_extent_enter = 153;
+    Ext4EsLookupExtentExitFtraceEvent ext4_es_lookup_extent_exit = 154;
+    Ext4EsRemoveExtentFtraceEvent ext4_es_remove_extent = 155;
+    Ext4EsShrinkFtraceEvent ext4_es_shrink = 156;
+    Ext4EsShrinkCountFtraceEvent ext4_es_shrink_count = 157;
+    Ext4EsShrinkScanEnterFtraceEvent ext4_es_shrink_scan_enter = 158;
+    Ext4EsShrinkScanExitFtraceEvent ext4_es_shrink_scan_exit = 159;
+    Ext4EvictInodeFtraceEvent ext4_evict_inode = 160;
+    Ext4ExtConvertToInitializedEnterFtraceEvent
+        ext4_ext_convert_to_initialized_enter = 161;
+    Ext4ExtConvertToInitializedFastpathFtraceEvent
+        ext4_ext_convert_to_initialized_fastpath = 162;
+    Ext4ExtHandleUnwrittenExtentsFtraceEvent ext4_ext_handle_unwritten_extents =
+        163;
+    Ext4ExtInCacheFtraceEvent ext4_ext_in_cache = 164;
+    Ext4ExtLoadExtentFtraceEvent ext4_ext_load_extent = 165;
+    Ext4ExtMapBlocksEnterFtraceEvent ext4_ext_map_blocks_enter = 166;
+    Ext4ExtMapBlocksExitFtraceEvent ext4_ext_map_blocks_exit = 167;
+    Ext4ExtPutInCacheFtraceEvent ext4_ext_put_in_cache = 168;
+    Ext4ExtRemoveSpaceFtraceEvent ext4_ext_remove_space = 169;
+    Ext4ExtRemoveSpaceDoneFtraceEvent ext4_ext_remove_space_done = 170;
+    Ext4ExtRmIdxFtraceEvent ext4_ext_rm_idx = 171;
+    Ext4ExtRmLeafFtraceEvent ext4_ext_rm_leaf = 172;
+    Ext4ExtShowExtentFtraceEvent ext4_ext_show_extent = 173;
+    Ext4FallocateEnterFtraceEvent ext4_fallocate_enter = 174;
+    Ext4FallocateExitFtraceEvent ext4_fallocate_exit = 175;
+    Ext4FindDelallocRangeFtraceEvent ext4_find_delalloc_range = 176;
+    Ext4ForgetFtraceEvent ext4_forget = 177;
+    Ext4FreeBlocksFtraceEvent ext4_free_blocks = 178;
+    Ext4FreeInodeFtraceEvent ext4_free_inode = 179;
+    Ext4GetImpliedClusterAllocExitFtraceEvent
+        ext4_get_implied_cluster_alloc_exit = 180;
+    Ext4GetReservedClusterAllocFtraceEvent ext4_get_reserved_cluster_alloc =
+        181;
+    Ext4IndMapBlocksEnterFtraceEvent ext4_ind_map_blocks_enter = 182;
+    Ext4IndMapBlocksExitFtraceEvent ext4_ind_map_blocks_exit = 183;
+    Ext4InsertRangeFtraceEvent ext4_insert_range = 184;
+    Ext4InvalidatepageFtraceEvent ext4_invalidatepage = 185;
+    Ext4JournalStartFtraceEvent ext4_journal_start = 186;
+    Ext4JournalStartReservedFtraceEvent ext4_journal_start_reserved = 187;
+    Ext4JournalledInvalidatepageFtraceEvent ext4_journalled_invalidatepage =
+        188;
+    Ext4JournalledWriteEndFtraceEvent ext4_journalled_write_end = 189;
+    Ext4LoadInodeFtraceEvent ext4_load_inode = 190;
+    Ext4LoadInodeBitmapFtraceEvent ext4_load_inode_bitmap = 191;
+    Ext4MarkInodeDirtyFtraceEvent ext4_mark_inode_dirty = 192;
+    Ext4MbBitmapLoadFtraceEvent ext4_mb_bitmap_load = 193;
+    Ext4MbBuddyBitmapLoadFtraceEvent ext4_mb_buddy_bitmap_load = 194;
+    Ext4MbDiscardPreallocationsFtraceEvent ext4_mb_discard_preallocations = 195;
+    Ext4MbNewGroupPaFtraceEvent ext4_mb_new_group_pa = 196;
+    Ext4MbNewInodePaFtraceEvent ext4_mb_new_inode_pa = 197;
+    Ext4MbReleaseGroupPaFtraceEvent ext4_mb_release_group_pa = 198;
+    Ext4MbReleaseInodePaFtraceEvent ext4_mb_release_inode_pa = 199;
+    Ext4MballocAllocFtraceEvent ext4_mballoc_alloc = 200;
+    Ext4MballocDiscardFtraceEvent ext4_mballoc_discard = 201;
+    Ext4MballocFreeFtraceEvent ext4_mballoc_free = 202;
+    Ext4MballocPreallocFtraceEvent ext4_mballoc_prealloc = 203;
+    Ext4OtherInodeUpdateTimeFtraceEvent ext4_other_inode_update_time = 204;
+    Ext4PunchHoleFtraceEvent ext4_punch_hole = 205;
+    Ext4ReadBlockBitmapLoadFtraceEvent ext4_read_block_bitmap_load = 206;
+    Ext4ReadpageFtraceEvent ext4_readpage = 207;
+    Ext4ReleasepageFtraceEvent ext4_releasepage = 208;
+    Ext4RemoveBlocksFtraceEvent ext4_remove_blocks = 209;
+    Ext4RequestBlocksFtraceEvent ext4_request_blocks = 210;
+    Ext4RequestInodeFtraceEvent ext4_request_inode = 211;
+    Ext4SyncFsFtraceEvent ext4_sync_fs = 212;
+    Ext4TrimAllFreeFtraceEvent ext4_trim_all_free = 213;
+    Ext4TrimExtentFtraceEvent ext4_trim_extent = 214;
+    Ext4TruncateEnterFtraceEvent ext4_truncate_enter = 215;
+    Ext4TruncateExitFtraceEvent ext4_truncate_exit = 216;
+    Ext4UnlinkEnterFtraceEvent ext4_unlink_enter = 217;
+    Ext4UnlinkExitFtraceEvent ext4_unlink_exit = 218;
+    Ext4WriteBeginFtraceEvent ext4_write_begin = 219;
+    // removed field with id 220;
+    // removed field with id 221;
+    // removed field with id 222;
+    // removed field with id 223;
+    // removed field with id 224;
+    // removed field with id 225;
+    // removed field with id 226;
+    // removed field with id 227;
+    // removed field with id 228;
+    // removed field with id 229;
+    Ext4WriteEndFtraceEvent ext4_write_end = 230;
+    Ext4WritepageFtraceEvent ext4_writepage = 231;
+    Ext4WritepagesFtraceEvent ext4_writepages = 232;
+    Ext4WritepagesResultFtraceEvent ext4_writepages_result = 233;
+    Ext4ZeroRangeFtraceEvent ext4_zero_range = 234;
+    TaskNewtaskFtraceEvent task_newtask = 235;
+    TaskRenameFtraceEvent task_rename = 236;
+    SchedProcessExecFtraceEvent sched_process_exec = 237;
+    SchedProcessExitFtraceEvent sched_process_exit = 238;
+    SchedProcessForkFtraceEvent sched_process_fork = 239;
+    SchedProcessFreeFtraceEvent sched_process_free = 240;
+    SchedProcessHangFtraceEvent sched_process_hang = 241;
+    SchedProcessWaitFtraceEvent sched_process_wait = 242;
+    F2fsDoSubmitBioFtraceEvent f2fs_do_submit_bio = 243;
+    F2fsEvictInodeFtraceEvent f2fs_evict_inode = 244;
+    F2fsFallocateFtraceEvent f2fs_fallocate = 245;
+    F2fsGetDataBlockFtraceEvent f2fs_get_data_block = 246;
+    F2fsGetVictimFtraceEvent f2fs_get_victim = 247;
+    F2fsIgetFtraceEvent f2fs_iget = 248;
+    F2fsIgetExitFtraceEvent f2fs_iget_exit = 249;
+    F2fsNewInodeFtraceEvent f2fs_new_inode = 250;
+    F2fsReadpageFtraceEvent f2fs_readpage = 251;
+    F2fsReserveNewBlockFtraceEvent f2fs_reserve_new_block = 252;
+    F2fsSetPageDirtyFtraceEvent f2fs_set_page_dirty = 253;
+    F2fsSubmitWritePageFtraceEvent f2fs_submit_write_page = 254;
+    F2fsSyncFileEnterFtraceEvent f2fs_sync_file_enter = 255;
+    F2fsSyncFileExitFtraceEvent f2fs_sync_file_exit = 256;
+    F2fsSyncFsFtraceEvent f2fs_sync_fs = 257;
+    F2fsTruncateFtraceEvent f2fs_truncate = 258;
+    F2fsTruncateBlocksEnterFtraceEvent f2fs_truncate_blocks_enter = 259;
+    F2fsTruncateBlocksExitFtraceEvent f2fs_truncate_blocks_exit = 260;
+    F2fsTruncateDataBlocksRangeFtraceEvent f2fs_truncate_data_blocks_range =
+        261;
+    F2fsTruncateInodeBlocksEnterFtraceEvent f2fs_truncate_inode_blocks_enter =
+        262;
+    F2fsTruncateInodeBlocksExitFtraceEvent f2fs_truncate_inode_blocks_exit =
+        263;
+    F2fsTruncateNodeFtraceEvent f2fs_truncate_node = 264;
+    F2fsTruncateNodesEnterFtraceEvent f2fs_truncate_nodes_enter = 265;
+    F2fsTruncateNodesExitFtraceEvent f2fs_truncate_nodes_exit = 266;
+    F2fsTruncatePartialNodesFtraceEvent f2fs_truncate_partial_nodes = 267;
+    F2fsUnlinkEnterFtraceEvent f2fs_unlink_enter = 268;
+    F2fsUnlinkExitFtraceEvent f2fs_unlink_exit = 269;
+    F2fsVmPageMkwriteFtraceEvent f2fs_vm_page_mkwrite = 270;
+    F2fsWriteBeginFtraceEvent f2fs_write_begin = 271;
+    F2fsWriteCheckpointFtraceEvent f2fs_write_checkpoint = 272;
+    F2fsWriteEndFtraceEvent f2fs_write_end = 273;
+    AllocPagesIommuEndFtraceEvent alloc_pages_iommu_end = 274;
+    AllocPagesIommuFailFtraceEvent alloc_pages_iommu_fail = 275;
+    AllocPagesIommuStartFtraceEvent alloc_pages_iommu_start = 276;
+    AllocPagesSysEndFtraceEvent alloc_pages_sys_end = 277;
+    AllocPagesSysFailFtraceEvent alloc_pages_sys_fail = 278;
+    AllocPagesSysStartFtraceEvent alloc_pages_sys_start = 279;
+    DmaAllocContiguousRetryFtraceEvent dma_alloc_contiguous_retry = 280;
+    IommuMapRangeFtraceEvent iommu_map_range = 281;
+    IommuSecPtblMapRangeEndFtraceEvent iommu_sec_ptbl_map_range_end = 282;
+    IommuSecPtblMapRangeStartFtraceEvent iommu_sec_ptbl_map_range_start = 283;
+    IonAllocBufferEndFtraceEvent ion_alloc_buffer_end = 284;
+    IonAllocBufferFailFtraceEvent ion_alloc_buffer_fail = 285;
+    IonAllocBufferFallbackFtraceEvent ion_alloc_buffer_fallback = 286;
+    IonAllocBufferStartFtraceEvent ion_alloc_buffer_start = 287;
+    IonCpAllocRetryFtraceEvent ion_cp_alloc_retry = 288;
+    IonCpSecureBufferEndFtraceEvent ion_cp_secure_buffer_end = 289;
+    IonCpSecureBufferStartFtraceEvent ion_cp_secure_buffer_start = 290;
+    IonPrefetchingFtraceEvent ion_prefetching = 291;
+    IonSecureCmaAddToPoolEndFtraceEvent ion_secure_cma_add_to_pool_end = 292;
+    IonSecureCmaAddToPoolStartFtraceEvent ion_secure_cma_add_to_pool_start =
+        293;
+    IonSecureCmaAllocateEndFtraceEvent ion_secure_cma_allocate_end = 294;
+    IonSecureCmaAllocateStartFtraceEvent ion_secure_cma_allocate_start = 295;
+    IonSecureCmaShrinkPoolEndFtraceEvent ion_secure_cma_shrink_pool_end = 296;
+    IonSecureCmaShrinkPoolStartFtraceEvent ion_secure_cma_shrink_pool_start =
+        297;
+    KfreeFtraceEvent kfree = 298;
+    KmallocFtraceEvent kmalloc = 299;
+    KmallocNodeFtraceEvent kmalloc_node = 300;
+    KmemCacheAllocFtraceEvent kmem_cache_alloc = 301;
+    KmemCacheAllocNodeFtraceEvent kmem_cache_alloc_node = 302;
+    KmemCacheFreeFtraceEvent kmem_cache_free = 303;
+    MigratePagesEndFtraceEvent migrate_pages_end = 304;
+    MigratePagesStartFtraceEvent migrate_pages_start = 305;
+    MigrateRetryFtraceEvent migrate_retry = 306;
+    MmPageAllocFtraceEvent mm_page_alloc = 307;
+    MmPageAllocExtfragFtraceEvent mm_page_alloc_extfrag = 308;
+    MmPageAllocZoneLockedFtraceEvent mm_page_alloc_zone_locked = 309;
+    MmPageFreeFtraceEvent mm_page_free = 310;
+    MmPageFreeBatchedFtraceEvent mm_page_free_batched = 311;
+    MmPagePcpuDrainFtraceEvent mm_page_pcpu_drain = 312;
+    RssStatFtraceEvent rss_stat = 313;
+    IonHeapShrinkFtraceEvent ion_heap_shrink = 314;
+    IonHeapGrowFtraceEvent ion_heap_grow = 315;
+    FenceInitFtraceEvent fence_init = 316;
+    FenceDestroyFtraceEvent fence_destroy = 317;
+    FenceEnableSignalFtraceEvent fence_enable_signal = 318;
+    FenceSignaledFtraceEvent fence_signaled = 319;
+    ClkEnableFtraceEvent clk_enable = 320;
+    ClkDisableFtraceEvent clk_disable = 321;
+    ClkSetRateFtraceEvent clk_set_rate = 322;
+    BinderTransactionAllocBufFtraceEvent binder_transaction_alloc_buf = 323;
+    SignalDeliverFtraceEvent signal_deliver = 324;
+    SignalGenerateFtraceEvent signal_generate = 325;
+    OomScoreAdjUpdateFtraceEvent oom_score_adj_update = 326;
+    GenericFtraceEvent generic = 327;
+    MmEventRecordFtraceEvent mm_event_record = 328;
+    SysEnterFtraceEvent sys_enter = 329;
+    SysExitFtraceEvent sys_exit = 330;
+    ZeroFtraceEvent zero = 331;
+    GpuFrequencyFtraceEvent gpu_frequency = 332;
+    SdeTracingMarkWriteFtraceEvent sde_tracing_mark_write = 333;
+    MarkVictimFtraceEvent mark_victim = 334;
+    IonStatFtraceEvent ion_stat = 335;
+  }
+}
+
+// End of protos/perfetto/trace/ftrace/ftrace_event.proto
+
+// Begin of protos/perfetto/trace/ftrace/ftrace_event_bundle.proto
+
+// The result of tracing one or more ftrace data pages from a single per-cpu
+// kernel ring buffer. If collating multiple pages' worth of events, all of
+// them come from contiguous pages, with no kernel data loss in between.
+message FtraceEventBundle {
+  optional uint32 cpu = 1;
+  repeated FtraceEvent event = 2;
+  // Set to true if there was data loss between the last time we've read from
+  // the corresponding per-cpu kernel buffer, and the earliest event recorded
+  // in this bundle.
+  optional bool lost_events = 3;
+
+  // Optionally-enabled compact encoding of a batch of scheduling events. Only
+  // a subset of events & their fields is recorded.
+  // All fields (except comms) are stored in a structure-of-arrays form, one
+  // entry in each repeated field per event.
+  message CompactSched {
+    // Interned table of unique strings for this bundle.
+    repeated string intern_table = 5;
+
+    // Delta-encoded timestamps across all sched_switch events within this
+    // bundle. The first is absolute, each next one is relative to its
+    // predecessor.
+    repeated uint64 switch_timestamp = 1 [packed = true];
+    repeated int64 switch_prev_state = 2 [packed = true];
+    repeated int32 switch_next_pid = 3 [packed = true];
+    repeated int32 switch_next_prio = 4 [packed = true];
+    // One per event, index into |intern_table| corresponding to the
+    // next_comm field of the event.
+    repeated uint32 switch_next_comm_index = 6 [packed = true];
+
+    // Delta-encoded timestamps across all sched_waking events within this
+    // bundle. The first is absolute, each next one is relative to its
+    // predecessor.
+    repeated uint64 waking_timestamp = 7 [packed = true];
+    repeated int32 waking_pid = 8 [packed = true];
+    repeated int32 waking_target_cpu = 9 [packed = true];
+    repeated int32 waking_prio = 10 [packed = true];
+    // One per event, index into |intern_table| corresponding to the
+    // comm field of the event.
+    repeated uint32 waking_comm_index = 11 [packed = true];
+  }
+  optional CompactSched compact_sched = 4;
+}
+
+// End of protos/perfetto/trace/ftrace/ftrace_event_bundle.proto
+
+// Begin of protos/perfetto/trace/ftrace/ftrace_stats.proto
+
+// Per-CPU stats for the ftrace data source gathered from the kernel from
+// /sys/kernel/debug/tracing/per_cpu/cpuX/stats.
+message FtraceCpuStats {
+  // CPU index.
+  optional uint64 cpu = 1;
+
+  // Number of entries still in the kernel buffer. Ideally this should be close
+  // to zero, as events are consumed regularly and moved into the userspace
+  // buffers (or file).
+  optional uint64 entries = 2;
+
+  // Number of events lost in kernel buffers due to overwriting of old events
+  // before userspace had a chance to drain them.
+  optional uint64 overrun = 3;
+
+  // This should always be zero. If not the buffer size is way too small or
+  // something went wrong with the tracer.
+  optional uint64 commit_overrun = 4;
+
+  // Bytes actually read (not overwritten).
+  optional uint64 bytes_read = 5;
+
+  // The timestamp for the oldest event still in the ring buffer.
+  optional double oldest_event_ts = 6;
+
+  // The current timestamp.
+  optional double now_ts = 7;
+
+  // If the kernel buffer has overwrite mode disabled, this will show the number
+  // of new events that were lost because the buffer was full. This is similar
+  // to |overrun| but only for the overwrite=false case.
+  optional uint64 dropped_events = 8;
+
+  // The number of events read.
+  optional uint64 read_events = 9;
+}
+
+// Ftrace stats for all CPUs.
+message FtraceStats {
+  enum Phase {
+    UNSPECIFIED = 0;
+    START_OF_TRACE = 1;
+    END_OF_TRACE = 2;
   }
 
-  // Only when using |event_id|.
-  optional uint32 event_duration_ns = 3;
+  // Tells when stats were sampled. There should be one sample at the beginning
+  // of the trace and one sample at the end.
+  optional Phase phase = 1;
 
-  // Only when using |counter_id|.
-  optional int32 counter_value = 4;
-
-  // ID of the thread that emitted the event.
-  optional uint32 thread_id = 5;
-
-  // If true the meta-tracing ring buffer had overruns and hence some data is
-  // missing from this point.
-  optional bool has_overruns = 6;
+  // Per-CPU stats (one entry for each CPU).
+  repeated FtraceCpuStats cpu_stats = 2;
 }
 
-// End of protos/perfetto/trace/perfetto/perfetto_metatrace.proto
+// End of protos/perfetto/trace/ftrace/ftrace_stats.proto
 
-// Begin of protos/perfetto/trace/power/battery_counters.proto
+// Begin of protos/perfetto/trace/gpu/gpu_counter_event.proto
 
-message BatteryCounters {
-  // Battery capacity in microampere-hours(µAh). Also known as Coulomb counter.
-  optional int64 charge_counter_uah = 1;
+message GpuCounterEvent {
+  // The first trace packet of each session should include counter_spec.
+  optional GpuCounterDescriptor counter_descriptor = 1;
 
-  // Remaining battery capacity percentage of total capacity
-  optional float capacity_percent = 2;
-
-  // Instantaneous battery current in microamperes(µA).
-  // Positive values indicate net current entering the battery from a charge
-  // source, negative values indicate net current discharging from the battery.
-  optional int64 current_ua = 3;
-
-  // Instantaneous battery current in microamperes(µA).
-  optional int64 current_avg_ua = 4;
-}
-
-// End of protos/perfetto/trace/power/battery_counters.proto
-
-// Begin of protos/perfetto/trace/power/power_rails.proto
-
-message PowerRails {
-
-  message RailDescriptor {
-    // Index corresponding to the rail
-    optional uint32 index = 1;
-
-    // Name of the rail
-    optional string rail_name = 2;
-
-    // Name of the subsystem to which this rail belongs
-    optional string subsys_name = 3;
-
-    // Hardware sampling rate (Hz).
-    optional uint32 sampling_rate = 4;
+  message GpuCounter {
+    // required. Identifier for counter.
+    optional uint32 counter_id = 1;
+    // required. Value of the counter.
+    oneof value {
+      int64 int_value = 2;
+      double double_value = 3;
+    }
   }
+  repeated GpuCounter counters = 2;
 
-  // This is only emitted at the beginning of the trace.
-  repeated RailDescriptor rail_descriptor = 1;
-
-  message EnergyData {
-    // Index corresponding to RailDescriptor.index
-    optional uint32 index = 1;
-
-    // Time since device boot(CLOCK_BOOTTIME) in milli-seconds.
-    optional uint64 timestamp_ms = 2;
-
-    // Accumulated energy since device boot in microwatt-seconds (uWs).
-    optional uint64 energy = 3;
-  }
-
-  repeated EnergyData energy_data = 2;
+  // optional. Identifier for GPU in a multi-gpu device.
+  optional int32 gpu_id = 3;
 }
 
-// End of protos/perfetto/trace/power/power_rails.proto
+// End of protos/perfetto/trace/gpu/gpu_counter_event.proto
 
-// Begin of protos/perfetto/trace/profiling/heap_graph.proto
+// Begin of protos/perfetto/trace/gpu/gpu_log.proto
 
-message ObfuscatedMember {
-  optional string obfuscated_name = 1;
-  optional string deobfuscated_name = 2;
-}
-
-message ObfuscatedClass {
-  optional string obfuscated_name = 1;
-  optional string deobfuscated_name = 2;
-  repeated ObfuscatedMember obfuscated_members = 3;
-}
-
-message DeobfuscationMapping {
-  optional string package_name = 1;
-  optional int64 version_code = 2;
-  repeated ObfuscatedClass obfuscated_classes = 3;
-}
-
-message HeapGraphRoot {
-  enum Type {
-    ROOT_UNKNOWN = 0;
-    ROOT_JNI_GLOBAL = 1;
-    ROOT_JNI_LOCAL = 2;
-    ROOT_JAVA_FRAME = 3;
-    ROOT_NATIVE_STACK = 4;
-    ROOT_STICKY_CLASS = 5;
-    ROOT_THREAD_BLOCK = 6;
-    ROOT_MONITOR_USED = 7;
-    ROOT_THREAD_OBJECT = 8;
-    ROOT_INTERNED_STRING = 9;
-    ROOT_FINALIZING = 10;
-    ROOT_DEBUGGER = 11;
-    ROOT_REFERENCE_CLEANUP = 12;
-    ROOT_VM_INTERNAL = 13;
-    ROOT_JNI_MONITOR = 14;
+// Message for logging events GPU data producer.
+message GpuLog {
+  enum Severity {
+    LOG_SEVERITY_UNSPECIFIED = 0;
+    LOG_SEVERITY_VERBOSE = 1;
+    LOG_SEVERITY_DEBUG = 2;
+    LOG_SEVERITY_INFO = 3;
+    LOG_SEVERITY_WARNING = 4;
+    LOG_SEVERITY_ERROR = 5;
   };
-  // Objects retained by this root.
-  repeated uint64 object_ids = 1 [packed = true];
+  optional Severity severity = 1;
 
-  optional Type root_type = 2;
+  optional string tag = 2;
+
+  optional string log_message = 3;
 }
 
-message HeapGraphObject {
-  optional uint64 id = 1;
+// End of protos/perfetto/trace/gpu/gpu_log.proto
 
-  // Index for InternedData.type_names for the name of the type of this object.
-  optional uint64 type_id = 2;
+// Begin of protos/perfetto/trace/gpu/gpu_render_stage_event.proto
 
-  // Bytes occupied by this objects.
-  optional uint64 self_size = 3;
+// next id: 13
+message GpuRenderStageEvent {
+  // required. Unique ID for the event.
+  optional uint64 event_id = 1;
 
-  // Indices for InternedData.field_names for the name of the field referring
-  // to the object.
-  repeated uint64 reference_field_id = 4 [packed = true];
+  // optional. Duration of the event. This should be in the same clock domain as
+  // the timestamp of the packet. If unset, this is a single time point event.
+  optional uint64 duration = 2;
 
-  // Ids of the Object that is referred to.
-  repeated uint64 reference_object_id = 5 [packed = true];
+  // required. ID to a hardware queue description in the specifications.
+  optional int32 hw_queue_id = 3;
+
+  // required. ID to a render stage description in the specifications.
+  optional int32 stage_id = 4;
+
+  // required. GL context/VK device.
+  optional uint64 context = 5;
+
+  // optional. The render target for this event.
+  optional uint64 render_target_handle = 8;
+
+  // optional. The Vulkan render pass handle.
+  optional uint64 render_pass_handle = 9;
+
+  // optional. The Vulkan command buffer handle.
+  optional uint64 command_buffer_handle = 12;
+
+  // optional. Submission ID generated by the UMD. Should map 1:1 with a
+  // vkQueueSubmit.
+  optional uint32 submission_id = 10;
+
+  // optional. Additional data for the user. This may include attribs for
+  // the event like resource ids, shaders etc
+  message ExtraData {
+    optional string name = 1;
+    optional string value = 2;
+  }
+  repeated ExtraData extra_data = 6;
+
+  // The first trace packet of each session should include a Specifications
+  // to enumerate *all* IDs that will be used. The timestamp of this packet
+  // must be earlier than all other packets. Only one packet with Specifications
+  // is expected.
+  message Specifications {
+    message ContextSpec {
+      optional uint64 context = 1;
+      optional int32 pid = 2;
+    }
+    optional ContextSpec context_spec = 1;
+
+    message Description {
+      optional string name = 1;
+      optional string description = 2;
+    }
+
+    // Labels to categorize the hw Queue this event goes on.
+    repeated Description hw_queue = 2;
+
+    // Labels to categorize render stage(binning, render, compute etc).
+    repeated Description stage = 3;
+  }
+  optional Specifications specifications = 7;
+
+  // optional. Identifier for GPU in a multi-gpu device.
+  optional int32 gpu_id = 11;
+
+  // Extension for vendor's custom proto.
+  extensions 100;
 }
 
-message HeapGraph {
-  optional int32 pid = 1;
+// End of protos/perfetto/trace/gpu/gpu_render_stage_event.proto
 
-  // This contains all objects at the time this dump was taken. Some of these
-  // will be live, some of those unreachable (garbage). To find the live
-  // objects, the client needs to build the transitive closure of objects
-  // reachable from |roots|.
-  // All objects not contained within that transitive closure are garbage that
-  // has not yet been collected.
-  repeated HeapGraphObject objects = 2;
+// Begin of protos/perfetto/trace/gpu/vulkan_api_event.proto
 
-  // Roots at the time this dump was taken.
-  // All live objects are reachable from the roots. All other objects are
-  // garbage.
-  repeated HeapGraphRoot roots = 7;
+// Message for recording the Vulkan call.
+message VulkanApiEvent {
+  oneof event {
+    VkDebugUtilsObjectName vk_debug_utils_object_name = 1;
+    VkQueueSubmit vk_queue_submit = 2;
+  }
 
-  // Type names used in managed heap graph.
-  repeated InternedString type_names = 3;
+  // For recording vkSetDebugUtilsObjectNameEXT and
+  // vkDebugMarkerSetObjectNameEXT
+  message VkDebugUtilsObjectName {
+    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.
+    optional int32 object_type = 3;
+    optional uint64 object = 4;
+    optional string object_name = 5;
+  }
 
-  // Field names for references in managed heap graph.
-  repeated InternedString field_names = 4;
-
-  optional bool continued = 5;
-  optional uint64 index = 6;
+  // 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/profiling/heap_graph.proto
+// End of protos/perfetto/trace/gpu/vulkan_api_event.proto
+
+// Begin of protos/perfetto/trace/gpu/vulkan_memory_event.proto
+
+// All the information that cannot be sent within a VulkanMemoryEvent message,
+// are sent as annotations to the main memory event. One example is the
+// properties of the object that consumes the allocated memory, for example, a
+// buffer or an image.
+// key_iid and string_iid are both interned strings. Original string value is
+// stored in vulkan_memory_keys from
+// protos/perfetto/trace/interned_data/interned_data.proto.
+message VulkanMemoryEventAnnotation {
+  optional uint64 key_iid = 1;
+  oneof value {
+    int64 int_value = 2;
+    double double_value = 3;
+    uint64 string_iid = 4;
+  }
+}
+
+// Each VulkanMemoryEvent encompasses information regarding one single function
+// call that results in reserving, binding or freeing host or GPU memory. There
+// is a special message type, ANNOTATIONS, which is used to communicate
+// information that are not directly related to a memory event, nonetheless are
+// essential to understand the memory usage. An example is the size and memory
+// types of the memory heaps.
+//
+// Next reserved id: 10 (up to 15).
+// Next id: 21.
+message VulkanMemoryEvent {
+  enum Source {
+    SOURCE_UNSPECIFIED = 0;
+    SOURCE_DRIVER = 1;
+    SOURCE_DEVICE = 2;
+    SOURCE_DEVICE_MEMORY = 3;
+    SOURCE_BUFFER = 4;
+    SOURCE_IMAGE = 5;
+  }
+
+  enum Operation {
+    OP_UNSPECIFIED = 0;
+    OP_CREATE = 1;         // alloc, create
+    OP_DESTROY = 2;        // free, destroy (non-bound)
+    OP_BIND = 3;           // bind buffer and image
+    OP_DESTROY_BOUND = 4;  // destroy (bound)
+    OP_ANNOTATIONS = 5;    // only annotations
+  }
+
+  enum AllocationScope {
+    SCOPE_UNSPECIFIED = 0;
+    SCOPE_COMMAND = 1;
+    SCOPE_OBJECT = 2;
+    SCOPE_CACHE = 3;
+    SCOPE_DEVICE = 4;
+    SCOPE_INSTANCE = 5;
+  }
+
+  optional Source source = 1;
+  optional Operation operation = 2;
+  optional int64 timestamp = 3;
+  optional uint32 pid = 4;
+  optional fixed64 memory_address = 5;
+  optional uint64 memory_size = 6;
+  // Interned string. Original string value is stored in function_names from
+  // protos/perfetto/trace/interned_data/interned_data.proto.
+  optional uint64 caller_iid = 7;
+  optional AllocationScope allocation_scope = 8;
+  // Extra related information, e.g., create configs, etc.
+  repeated VulkanMemoryEventAnnotation annotations = 9;
+
+  // Field IDs used for device memory (low sampling rate)
+  optional fixed64 device = 16;
+  optional fixed64 device_memory = 17;
+  optional uint32 memory_type = 18;
+  optional uint32 heap = 19;
+  optional fixed64 object_handle = 20;
+}
+
+// End of protos/perfetto/trace/gpu/vulkan_memory_event.proto
 
 // Begin of protos/perfetto/trace/profiling/profile_common.proto
 
@@ -3241,506 +4854,97 @@
 
 // End of protos/perfetto/trace/profiling/profile_common.proto
 
-// Begin of protos/perfetto/trace/profiling/profile_packet.proto
+// Begin of protos/perfetto/trace/track_event/debug_annotation.proto
 
-message ProfilePacket {
-  // The following interning tables are only used in Android version Q.
-  // In newer versions, these tables are in InternedData
-  // (see protos/perfetto/trace/interned_data) and are shared across
-  // multiple ProfilePackets.
-  // For backwards compatibility, consumers need to first look up interned
-  // data in the tables within the ProfilePacket, and then, if they are empty,
-  // look up in the InternedData instead.
-  repeated InternedString strings = 1;
-  repeated Mapping mappings = 4;
-  repeated Frame frames = 2;
-  repeated Callstack callstacks = 3;
-
-  // Next ID: 9
-  message HeapSample {
-    optional uint64 callstack_id = 1;
-    // bytes allocated at this callstack.
-    optional uint64 self_allocated = 2;
-    // bytes allocated at this callstack that have been freed.
-    optional uint64 self_freed = 3;
-    // bytes allocated at this callstack but not used since the last
-    // dump.
-    // See documentation of idle_allocations in HeapprofdConfig for more
-    // details.
-    optional uint64 self_idle = 7;
-    // Bytes allocated by this callstack but not freed at the time the malloc
-    // heap usage of this process was maximal. This is only set if dump_at_max
-    // is true in HeapprofdConfig. In that case, self_allocated, self_freed and
-    // self_idle will not be set.
-    optional uint64 self_max = 8;
-    optional uint64 timestamp = 4;  // timestamp [opt]
-    // Number of allocations that were sampled at this callstack.
-    optional uint64 alloc_count = 5;
-    // Number of allocations that were sampled at this callstack that have been
-    // freed.
-    optional uint64 free_count = 6;
-  }
-
-  message Histogram {
-    message Bucket {
-      // This bucket counts values from the previous bucket's (or -infinity if
-      // this is the first bucket) upper_limit (inclusive) to this upper_limit
-      // (exclusive).
-      optional uint64 upper_limit = 1;
-      // This is the highest bucket. This is set instead of the upper_limit. Any
-      // values larger or equal to the previous bucket's upper_limit are counted
-      // in this bucket.
-      optional bool max_bucket = 2;
-      // Number of values that fall into this range.
-      optional uint64 count = 3;
+// Key/value annotations provided in untyped TRACE_EVENT macros. These
+// annotations are intended for debug use and are not considered a stable API
+// surface. As such, they should not be relied upon to implement (new) metrics.
+//
+// Next ID: 10.
+message DebugAnnotation {
+  message NestedValue {
+    enum NestedType {
+      UNSPECIFIED = 0;  // leaf value.
+      DICT = 1;
+      ARRAY = 2;
     }
-    repeated Bucket buckets = 1;
+    optional NestedType nested_type = 1;
+
+    repeated string dict_keys = 2;
+    repeated NestedValue dict_values = 3;
+    repeated NestedValue array_values = 4;
+    optional int64 int_value = 5;
+    optional double double_value = 6;
+    optional bool bool_value = 7;
+    optional string string_value = 8;
   }
 
-  message ProcessStats {
-    optional uint64 unwinding_errors = 1;
-    optional uint64 heap_samples = 2;
-    optional uint64 map_reparses = 3;
-    optional Histogram unwinding_time_us = 4;
-    optional uint64 total_unwinding_time_us = 5;
+  oneof name_field {
+    uint64 name_iid = 1;  // interned DebugAnnotationName.
+    string name = 10;     // non-interned variant.
   }
 
-  repeated ProcessHeapSamples process_dumps = 5;
-  message ProcessHeapSamples {
-    optional uint64 pid = 1;
+  oneof value {
+    bool bool_value = 2;
+    uint64 uint_value = 3;
+    int64 int_value = 4;
+    double double_value = 5;
+    string string_value = 6;
+    // Pointers are stored in a separate type as the JSON output treats them
+    // differently from other uint64 values.
+    uint64 pointer_value = 7;
+    NestedValue nested_value = 8;
 
-    // This process was profiled from startup.
-    // If false, this process was already running when profiling started.
-    optional bool from_startup = 3;
-
-    // This process was not profiled because a concurrent session was active.
-    // If this is true, samples will be empty.
-    optional bool rejected_concurrent = 4;
-
-    // This process disconnected while it was profiled.
-    // If false, the process outlived the profiling session.
-    optional bool disconnected = 6;
-
-    // If disconnected, this disconnect was caused by the client overrunning
-    // the buffer.
-    optional bool buffer_overran = 7;
-
-    // If disconnected, this disconnected was caused by the shared memory
-    // buffer being corrupted. THIS IS ALWAYS A BUG IN HEAPPROFD OR CLIENT
-    // MEMORY CORRUPTION.
-    optional bool buffer_corrupted = 8;
-
-    // Timestamp of the state of the target process that this dump represents.
-    // This can be different to the timestamp of the TracePackets for various
-    // reasons:
-    // * If disconnected is set above, this is the timestamp of last state
-    //   heapprofd had of the process before it disconnected.
-    // * Otherwise, if the rate of events produced by the process is high,
-    //   heapprofd might be behind.
-    //
-    // TODO(fmayer): This is MONOTONIC_COARSE. Refactor ClockSnapshot::Clock
-    //               to have a type enum that we can reuse here.
-    optional uint64 timestamp = 9;
-
-    // Metadata about heapprofd.
-    optional ProcessStats stats = 5;
-
-    repeated HeapSample samples = 2;
+    // Legacy instrumentation may not support conversion of nested data to
+    // NestedValue yet.
+    string legacy_json_value = 9;
   }
-
-  // If this is true, the next ProfilePacket in this package_sequence_id is a
-  // continuation of this one.
-  // To get all samples for a process, accummulate its
-  // ProcessHeapSamples.samples until you see continued=false.
-  optional bool continued = 6;
-
-  // Index of this ProfilePacket on its package_sequence_id. Can be used
-  // to detect dropped data.
-  // Verify these are consecutive.
-  optional uint64 index = 7;
 }
 
-// Message used to represent individual stack samples sampled at discrete
-// points in time, rather than aggregated over an interval.
-message StreamingProfilePacket {
-  repeated uint64 callstack_iid = 1;  // Index into InternedData.callstacks
-  repeated int64 timestamp_delta_us = 2;
+// --------------------
+// Interned data types:
+// --------------------
+
+message DebugAnnotationName {
+  optional uint64 iid = 1;
+  optional string name = 2;
 }
 
-// End of protos/perfetto/trace/profiling/profile_packet.proto
+// End of protos/perfetto/trace/track_event/debug_annotation.proto
 
-// Begin of protos/perfetto/trace/ps/process_stats.proto
+// Begin of protos/perfetto/trace/track_event/log_message.proto
 
-// Per-process periodically sampled stats. These samples are wrapped in a
-// dedicated message (as opposite to be fields in process_tree.proto) because
-// they are dumped at a different rate than cmdline and thread list.
-// Note: not all of these stats will be present in every ProcessStats message
-// and sometimes processes may be missing . This is because counters are
-// cached to reduce emission of counters which do not change.
-message ProcessStats {
-  message Process {
-    optional int32 pid = 1;
-
-    // See /proc/[pid]/status in `man 5 proc` for a description of these fields.
-    optional uint64 vm_size_kb = 2;
-    optional uint64 vm_rss_kb = 3;
-    optional uint64 rss_anon_kb = 4;
-    optional uint64 rss_file_kb = 5;
-    optional uint64 rss_shmem_kb = 6;
-    optional uint64 vm_swap_kb = 7;
-    optional uint64 vm_locked_kb = 8;
-    optional uint64 vm_hwm_kb = 9;
-    // When adding a new field remember to update kProcMemCounterSize in
-    // the trace processor.
-
-    optional int64 oom_score_adj = 10;
-  }
-  repeated Process processes = 1;
-
-  // The time at which we finish collecting this batch of samples;
-  // the top-level packet timestamp is the time at which
-  // we begin collection.
-  // TODO(dancol): analysis might be improved by
-  // time-bracketing each sample as well as the whole
-  // ProcessStats, but doing that is probably gated on
-  // a vdso for CLOCK_BOOTTIME.
-  optional uint64 collection_end_timestamp = 2;
+message LogMessage {
+  optional uint64 source_location_iid = 1;  // interned SourceLocation.
+  optional uint64 body_iid = 2;             // interned LogMessageBody.
 }
 
-// End of protos/perfetto/trace/ps/process_stats.proto
+// --------------------
+// Interned data types:
+// --------------------
 
-// Begin of protos/perfetto/trace/ps/process_tree.proto
+message LogMessageBody {
+  optional uint64 iid = 1;
+  optional string body = 2;
+}
+// End of protos/perfetto/trace/track_event/log_message.proto
 
-message ProcessTree {
-  // Representation of a thread.
-  message Thread {
-    // The thread id (as per gettid())
-    optional int32 tid = 1;
+// Begin of protos/perfetto/trace/track_event/source_location.proto
 
-    // Thread group id (i.e. the PID of the process, == TID of the main thread)
-    optional int32 tgid = 3;
+// --------------------
+// Interned data types:
+// --------------------
 
-    // The name of the thread.
-    optional string name = 2;
-  }
+message SourceLocation {
+  optional uint64 iid = 1;
 
-  // Representation of a process.
-  message Process {
-    // The UNIX process ID, aka thread group ID (as per getpid()).
-    optional int32 pid = 1;
-
-    // The parent process ID, as per getppid().
-    optional int32 ppid = 2;
-
-    // The command line for the process, as per /proc/pid/cmdline.
-    // If it is a kernel thread there will only be one cmdline field
-    // and it will contain /proc/pid/comm.
-    repeated string cmdline = 3;
-
-    // No longer used as of Apr 2018, when the dedicated |threads| field was
-    // introduced in ProcessTree.
-    repeated Thread threads_deprecated = 4 [deprecated = true];
-
-    // The uid for the process, as per /proc/pid/status.
-    optional int32 uid = 5;
-  }
-
-  // List of processes and threads in the client. These lists are incremental
-  // and not exhaustive. A process and its threads might show up separately in
-  // different ProcessTree messages. A thread might event not show up at all, if
-  // no sched_switch activity was detected, for instance:
-  // #0 { processes: [{pid: 10, ...}], threads: [{pid: 11, tgid: 10}] }
-  // #1 { threads: [{pid: 12, tgid: 10}] }
-  // #2 { processes: [{pid: 20, ...}], threads: [{pid: 13, tgid: 10}] }
-  repeated Process processes = 1;
-  repeated Thread threads = 2;
-
-  // The time at which we finish collecting this process tree;
-  // the top-level packet timestamp is the time at which
-  // we begin collection.
-  optional uint64 collection_end_timestamp = 3;
+  // We intend to add a binary symbol version of this in the future.
+  optional string file_name = 2;
+  optional string function_name = 3;
+  optional uint32 line_number = 4;
 }
 
-// End of protos/perfetto/trace/ps/process_tree.proto
-
-// Begin of protos/perfetto/trace/sys_stats/sys_stats.proto
-
-// Various Linux system stat counters from /proc.
-// The fields in this message can be reported at different rates and with
-// different granularity. See sys_stats_config.proto.
-message SysStats {
-  // Counters from /proc/meminfo. Values are in KB.
-  message MeminfoValue {
-    optional MeminfoCounters key = 1;
-    optional uint64 value = 2;
-  };
-  repeated MeminfoValue meminfo = 1;
-
-  // Counter from /proc/vmstat. Units are often pages, not KB.
-  message VmstatValue {
-    optional VmstatCounters key = 1;
-    optional uint64 value = 2;
-  };
-  repeated VmstatValue vmstat = 2;
-
-  // Times in each mode, since boot. Unit: nanoseconds.
-  message CpuTimes {
-    optional uint32 cpu_id = 1;
-    optional uint64 user_ns = 2;         // Time spent in user mode.
-    optional uint64 user_ice_ns = 3;     // Time spent in user mode (low prio).
-    optional uint64 system_mode_ns = 4;  // Time spent in system mode.
-    optional uint64 idle_ns = 5;         // Time spent in the idle task.
-    optional uint64 io_wait_ns = 6;      // Time spent waiting for I/O.
-    optional uint64 irq_ns = 7;          // Time spent servicing interrupts.
-    optional uint64 softirq_ns = 8;      // Time spent servicing softirqs.
-  }
-  repeated CpuTimes cpu_stat = 3;  // One entry per cpu.
-
-  // Num processes forked since boot.
-  // Populated only if FORK_COUNT in config.stat_counters.
-  optional uint64 num_forks = 4;
-
-  message InterruptCount {
-    optional int32 irq = 1;
-    optional uint64 count = 2;
-  }
-
-  // Number of interrupts, broken by IRQ number.
-  // Populated only if IRQ_COUNTS in config.stat_counters.
-  optional uint64 num_irq_total = 5;  // Total num of irqs serviced since boot.
-  repeated InterruptCount num_irq = 6;
-
-  // Number of softirqs, broken by softirq number.
-  // Populated only if SOFTIRQ_COUNTS in config.stat_counters.
-  optional uint64 num_softirq_total = 7;    // Total num of softirqs since boot.
-  repeated InterruptCount num_softirq = 8;  // Per-softirq count.
-
-  // The time at which we finish collecting this set of samples;
-  // the top-level packet timestamp is the time at which
-  // we begin collection.
-  optional uint64 collection_end_timestamp = 9;
-}
-
-// End of protos/perfetto/trace/sys_stats/sys_stats.proto
-
-// Begin of protos/perfetto/trace/system_info.proto
-
-message Utsname {
-  optional string sysname = 1;
-  optional string version = 2;
-  optional string release = 3;
-  optional string machine = 4;
-}
-
-message SystemInfo {
-  optional Utsname utsname = 1;
-  optional string android_build_fingerprint = 2;
-}
-
-// End of protos/perfetto/trace/system_info.proto
-
-// Begin of protos/perfetto/trace/trace.proto
-
-message Trace {
-  repeated TracePacket packet = 1;
-
-  // Do NOT add any other field here. This is just a convenience wrapper for
-  // the use case of a trace being saved to a file. There are other cases
-  // (streaming) where TracePacket are directly streamed without being wrapped
-  // in a Trace proto. Nothing should ever rely on the full trace, all the
-  // logic should be based on TracePacket(s).
-}
-
-// End of protos/perfetto/trace/trace.proto
-
-// Begin of protos/perfetto/trace/trace_packet.proto
-
-// The root object emitted by Perfetto. A perfetto trace is just a stream of
-// TracePacket(s).
-//
-// Next reserved id: 13 (up to 15).
-// Next id: 66.
-message TracePacket {
-  // The timestamp of the TracePacket.
-  // By default this timestamps refers to the trace clock (CLOCK_BOOTTIME on
-  // Android). It can be overridden using a different timestamp_clock_id.
-  // The clock domain definition in ClockSnapshot can also override:
-  // - The unit (default: 1ns).
-  // - The absolute vs delta encoding (default: absolute timestamp).
-  optional uint64 timestamp = 8;
-
-  // Specifies the ID of the clock used for the TracePacket |timestamp|. Can be
-  // one of the built-in types from ClockSnapshot::BuiltinClocks, or a
-  // producer-defined clock id.
-  // If unspecified and if no default per-sequence value has been provided via
-  // TracePacketDefaults, it defaults to BuiltinClocks::BOOTTIME.
-  optional uint32 timestamp_clock_id = 58;
-
-  oneof data {
-    FtraceEventBundle ftrace_events = 1;
-    ProcessTree process_tree = 2;
-    ProcessStats process_stats = 9;
-    InodeFileMap inode_file_map = 4;
-    // removed field with id 5
-    ClockSnapshot clock_snapshot = 6;
-    SysStats sys_stats = 7;
-    TrackEvent track_event = 11;
-
-    // IDs up to 15 are reserved. They take only one byte to encode their
-    // preamble so should be used for freqeuent events.
-
-    TraceConfig trace_config = 33;
-    FtraceStats ftrace_stats = 34;
-    TraceStats trace_stats = 35;
-    ProfilePacket profile_packet = 37;
-    BatteryCounters battery = 38;
-    PowerRails power_rails = 40;
-    AndroidLogPacket android_log = 39;
-    SystemInfo system_info = 45;
-    Trigger trigger = 46;
-    PackagesList packages_list = 47;
-    ChromeBenchmarkMetadata chrome_benchmark_metadata = 48;
-    PerfettoMetatrace perfetto_metatrace = 49;
-    ChromeMetadataPacket chrome_metadata = 51;
-    GpuCounterEvent gpu_counter_event = 52;
-    GpuRenderStageEvent gpu_render_stage_event = 53;
-    StreamingProfilePacket streaming_profile_packet = 54;
-    HeapGraph heap_graph = 56;
-    GraphicsFrameEvent graphics_frame_event = 57;
-    // removed field with id 62
-    GpuLog gpu_log = 63;
-    VulkanApiEvent vulkan_api_event = 65;
-
-    // Only used in profile packets.
-    ProfiledFrameSymbols profiled_frame_symbols = 55;
-    ModuleSymbols module_symbols = 61;
-    DeobfuscationMapping deobfuscation_mapping = 64;
-
-    // Only used by TrackEvent.
-    TrackDescriptor track_descriptor = 60;
-
-    // Deprecated, use TrackDescriptor instead.
-    ProcessDescriptor process_descriptor = 43;
-
-    // Deprecated, use TrackDescriptor instead.
-    ThreadDescriptor thread_descriptor = 44;
-
-    // This field is emitted at periodic intervals (~10s) and
-    // contains always the binary representation of the UUID
-    // {82477a76-b28d-42ba-81dc-33326d57a079}. This is used to be able to
-    // efficiently partition long traces without having to fully parse them.
-    bytes synchronization_marker = 36;
-
-    // Zero or more proto encoded trace packets compressed using deflate.
-    // Each compressed_packets TracePacket (including the two field ids and
-    // sizes) should be less than 512KB.
-    bytes compressed_packets = 50;
-
-    // This field is only used for testing.
-    // In previous versions of this proto this field had the id 268435455
-    // This caused many problems:
-    // - protozero decoder does not handle field ids larger than 999.
-    // - old versions of protoc produce Java bindings with syntax errors when
-    //   the field id is large enough.
-    // removed field with id 900
-  }
-
-  // Trusted user id of the producer which generated this packet. Keep in sync
-  // with TrustedPacket.trusted_uid.
-  //
-  // TODO(eseckler): Emit this field in a PacketSequenceDescriptor message
-  // instead.
-  oneof optional_trusted_uid { int32 trusted_uid = 3; };
-
-  // Service-assigned identifier of the packet sequence this packet belongs to.
-  // Uniquely identifies a producer + writer pair within the tracing session. A
-  // value of zero denotes an invalid ID. Keep in sync with
-  // TrustedPacket.trusted_packet_sequence_id.
-  oneof optional_trusted_packet_sequence_id {
-    uint32 trusted_packet_sequence_id = 10;
-  }
-
-  // Incrementally emitted interned data, valid only on the packet's sequence
-  // (packets with the same |trusted_packet_sequence_id|). The writer will
-  // usually emit new interned data in the same TracePacket that first refers to
-  // it (since the last reset of interning state). It may also be emitted
-  // proactively in advance of referring to them in later packets.
-  optional InternedData interned_data = 12;
-
-
-  enum SequenceFlags {
-    SEQ_UNSPECIFIED = 0;
-
-    // Set by the writer to indicate that it will re-emit any incremental data
-    // for the packet's sequence before referring to it again. This includes
-    // interned data as well as periodically emitted data like
-    // Process/ThreadDescriptors. This flag only affects the current packet
-    // sequence (see |trusted_packet_sequence_id|).
-    //
-    // When set, this TracePacket and subsequent TracePackets on the same
-    // sequence will not refer to any incremental data emitted before this
-    // TracePacket. For example, previously emitted interned data will be
-    // re-emitted if it is referred to again.
-    //
-    // When the reader detects packet loss (|previous_packet_dropped|), it needs
-    // to skip packets in the sequence until the next one with this flag set, to
-    // ensure intact incremental data.
-    SEQ_INCREMENTAL_STATE_CLEARED = 1;
-
-    // This packet requires incremental state, such as TracePacketDefaults or
-    // InternedData, to be parsed correctly. The trace reader should skip this
-    // packet if incremental state is not valid on this sequence, i.e. if no
-    // packet with the SEQ_INCREMENTAL_STATE_CLEARED flag has been seen on the
-    // current |trusted_packet_sequence_id|.
-    SEQ_NEEDS_INCREMENTAL_STATE = 2;
-  };
-  optional uint32 sequence_flags = 13;
-
-  // DEPRECATED. Moved to SequenceFlags::SEQ_INCREMENTAL_STATE_CLEARED.
-  optional bool incremental_state_cleared = 41;
-
-  // Default values for fields of later TracePackets emitted on this packet's
-  // sequence (TracePackets with the same |trusted_packet_sequence_id|).
-  // It must be reemitted when incremental state is cleared (see
-  // |incremental_state_cleared|).
-  // Requires that any future packet emitted on the same sequence specifies
-  // the SEQ_NEEDS_INCREMENTAL_STATE flag.
-  // TracePacketDefaults always override the global defaults for any future
-  // packet on this sequence (regardless of SEQ_NEEDS_INCREMENTAL_STATE).
-  optional TracePacketDefaults trace_packet_defaults = 59;
-
-  // Flag set by the service if, for the current packet sequence (see
-  // |trusted_packet_sequence_id|), either:
-  // * this is the first packet, or
-  // * one or multiple packets were dropped since the last packet that the
-  //   consumer read from the sequence. This can happen if chunks in the trace
-  //   buffer are overridden before the consumer could read them when the trace
-  //   is configured in ring buffer mode.
-  //
-  // When packet loss occurs, incrementally emitted data (including interned
-  // data) on the sequence should be considered invalid up until the next packet
-  // with SEQ_INCREMENTAL_STATE_CLEARED set.
-  optional bool previous_packet_dropped = 42;
-}
-
-// End of protos/perfetto/trace/trace_packet.proto
-
-// Begin of protos/perfetto/trace/trace_packet_defaults.proto
-
-// Default values for TracePacket fields that hold for a particular TraceWriter
-// packet sequence. This message contains a subset of the TracePacket fields
-// with matching IDs. When provided, these fields define the default values
-// that should be applied, at import time, to all TracePacket(s) with the same
-// |trusted_packet_sequence_id|, unless otherwise specified in each packet.
-//
-// Should be reemitted whenever incremental state is cleared on the sequence.
-message TracePacketDefaults {
-  optional uint32 timestamp_clock_id = 58;
-
-  // Default values for TrackEvents (e.g. default track).
-  optional TrackEventDefaults track_event_defaults = 11;
-}
-// End of protos/perfetto/trace/trace_packet_defaults.proto
+// End of protos/perfetto/trace/track_event/source_location.proto
 
 // Begin of protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto
 
@@ -4015,6 +5219,74 @@
 
 // End of protos/perfetto/trace/track_event/chrome_keyed_service.proto
 
+// Begin of protos/perfetto/trace/track_event/chrome_latency_info.proto
+
+message ChromeLatencyInfo {
+  optional int64 trace_id = 1;
+
+  // NEXT ID: 11
+  // All step are optional but the enum is ordered (not by number) below in the
+  // order we expect them to appear if they are emitted in trace in a blocking
+  // fashion.
+  enum Step {
+    STEP_UNSPECIFIED = 0;
+    // Emitted on the browser main thread.
+    STEP_SEND_INPUT_EVENT_UI = 3;
+    // Happens on the renderer's compositor.
+    STEP_HANDLE_INPUT_EVENT_IMPL = 5;
+    STEP_DID_HANDLE_INPUT_AND_OVERSCROLL = 8;
+    // Occurs on the Renderer's main thread.
+    STEP_HANDLE_INPUT_EVENT_MAIN = 4;
+    STEP_MAIN_THREAD_SCROLL_UPDATE = 2;
+    STEP_HANDLE_INPUT_EVENT_MAIN_COMMIT = 1;
+    // Could be emitted on both the renderer's main OR compositor.
+    STEP_HANDLED_INPUT_EVENT_MAIN_OR_IMPL = 9;
+    // Optionally sometimes HANDLED_INPUT_EVENT_MAIN_OR_IMPL will proxy to the
+    // renderer's compositor and this will be emitted.
+    STEP_HANDLED_INPUT_EVENT_IMPL = 10;
+    // Renderer's compositor.
+    STEP_SWAP_BUFFERS = 6;
+    // Happens on the VizCompositor in the GPU process.
+    STEP_DRAW_AND_SWAP = 7;
+  };
+
+  optional Step step = 2;
+  optional int32 frame_tree_node_id = 3;
+
+  // This enum is a copy of LatencyComponentType enum in Chrome, located in
+  // ui/latency/latency_info.h, modulo added UNKNOWN value per protobuf
+  // practices.
+  enum LatencyComponentType {
+    COMPONENT_UNSPECIFIED = 0;
+    COMPONENT_INPUT_EVENT_LATENCY_BEGIN_RWH = 1;
+    COMPONENT_INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL = 2;
+    COMPONENT_INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL = 3;
+    COMPONENT_INPUT_EVENT_LATENCY_ORIGINAL = 4;
+    COMPONENT_INPUT_EVENT_LATENCY_UI = 5;
+    COMPONENT_INPUT_EVENT_LATENCY_RENDERER_MAIN = 6;
+    COMPONENT_INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_MAIN = 7;
+    COMPONENT_INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL = 8;
+    COMPONENT_INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT = 9;
+    COMPONENT_INPUT_EVENT_LATENCY_ACK_RWH = 10;
+    COMPONENT_INPUT_EVENT_LATENCY_RENDERER_SWAP = 11;
+    COMPONENT_DISPLAY_COMPOSITOR_RECEIVED_FRAME = 12;
+    COMPONENT_INPUT_EVENT_GPU_SWAP_BUFFER = 13;
+    COMPONENT_INPUT_EVENT_LATENCY_FRAME_SWAP = 14;
+  }
+
+  message ComponentInfo {
+    optional LatencyComponentType component_type = 1;
+
+    // Microsecond timestamp in CLOCK_MONOTONIC domain
+    optional uint64 time_us = 2;
+  };
+
+  repeated ComponentInfo component_info = 4;
+  optional bool is_coalesced = 5;
+}
+
+// End of protos/perfetto/trace/track_event/chrome_latency_info.proto
+
 // Begin of protos/perfetto/trace/track_event/chrome_legacy_ipc.proto
 
 // Details about a legacy Chrome IPC message that is either sent by the event.
@@ -4072,75 +5344,6 @@
 
 // End of protos/perfetto/trace/track_event/chrome_legacy_ipc.proto
 
-// Begin of protos/perfetto/trace/track_event/chrome_process_descriptor.proto
-
-// Describes the attributes for a Chrome process. Must be paired with a
-// ProcessDescriptor in the same TrackDescriptor.
-//
-// Next id: 4.
-message ChromeProcessDescriptor {
-  // See chromium's content::ProcessType.
-  enum ProcessType {
-    PROCESS_UNSPECIFIED = 0;
-    PROCESS_BROWSER = 1;
-    PROCESS_RENDERER = 2;
-    PROCESS_UTILITY = 3;
-    PROCESS_ZYGOTE = 4;
-    PROCESS_SANDBOX_HELPER = 5;
-    PROCESS_GPU = 6;
-    PROCESS_PPAPI_PLUGIN = 7;
-    PROCESS_PPAPI_BROKER = 8;
-  }
-  optional ProcessType process_type = 1;
-  optional int32 process_priority = 2;
-
-  // To support old UI. New UI should determine default sorting by process_type.
-  optional int32 legacy_sort_index = 3;
-}
-
-// End of protos/perfetto/trace/track_event/chrome_process_descriptor.proto
-
-// Begin of protos/perfetto/trace/track_event/chrome_thread_descriptor.proto
-
-// Describes a Chrome thread's attributes. Emitted as part of a TrackDescriptor,
-// usually by the thread's trace writer. Must be paired with a ThreadDescriptor
-// in the same TrackDescriptor.
-//
-// Next id: 3.
-message ChromeThreadDescriptor {
-  enum ThreadType {
-    THREAD_UNSPECIFIED = 0;
-
-    THREAD_MAIN = 1;
-    THREAD_IO = 2;
-
-    // Scheduler:
-    THREAD_POOL_BG_WORKER = 3;
-    THREAD_POOL_FG_WORKER = 4;
-    THREAD_POOL_BG_BLOCKING = 6;
-    THREAD_POOL_FG_BLOCKING = 5;
-    THREAD_POOL_SERVICE = 7;
-
-    // Compositor:
-    THREAD_COMPOSITOR = 8;
-    THREAD_VIZ_COMPOSITOR = 9;
-    THREAD_COMPOSITOR_WORKER = 10;
-
-    // Renderer:
-    THREAD_SERVICE_WORKER = 11;
-
-    // Tracing related threads:
-    THREAD_MEMORY_INFRA = 50;
-    THREAD_SAMPLING_PROFILER = 51;
-  };
-  optional ThreadType thread_type = 1;
-
-  // To support old UI. New UI should determine default sorting by thread_type.
-  optional int32 legacy_sort_index = 2;
-}
-
-// End of protos/perfetto/trace/track_event/chrome_thread_descriptor.proto
-
 // Begin of protos/perfetto/trace/track_event/chrome_user_event.proto
 
 // Details about a UI interaction initiated by the user, such as opening or
@@ -4150,139 +5353,13 @@
   // Chrome, these are usually static strings known at compile time, or
   // concatenations of multiple such static strings).
   optional string action = 1;
+
+  // MD5 hash of the action string.
+  optional uint64 action_hash = 2;
 }
 
 // End of protos/perfetto/trace/track_event/chrome_user_event.proto
 
-// Begin of protos/perfetto/trace/track_event/debug_annotation.proto
-
-// Key/value annotations provided in untyped TRACE_EVENT macros. These
-// annotations are intended for debug use and are not considered a stable API
-// surface. As such, they should not be relied upon to implement (new) metrics.
-//
-// Next ID: 10.
-message DebugAnnotation {
-  message NestedValue {
-    enum NestedType {
-      UNSPECIFIED = 0;  // leaf value.
-      DICT = 1;
-      ARRAY = 2;
-    }
-    optional NestedType nested_type = 1;
-
-    repeated string dict_keys = 2;
-    repeated NestedValue dict_values = 3;
-    repeated NestedValue array_values = 4;
-    optional int64 int_value = 5;
-    optional double double_value = 6;
-    optional bool bool_value = 7;
-    optional string string_value = 8;
-  }
-
-  oneof name_field {
-    uint64 name_iid = 1;  // interned DebugAnnotationName.
-    string name = 10;     // non-interned variant.
-  }
-
-  oneof value {
-    bool bool_value = 2;
-    uint64 uint_value = 3;
-    int64 int_value = 4;
-    double double_value = 5;
-    string string_value = 6;
-    // Pointers are stored in a separate type as the JSON output treats them
-    // differently from other uint64 values.
-    uint64 pointer_value = 7;
-    NestedValue nested_value = 8;
-
-    // Legacy instrumentation may not support conversion of nested data to
-    // NestedValue yet.
-    string legacy_json_value = 9;
-  }
-}
-
-// --------------------
-// Interned data types:
-// --------------------
-
-message DebugAnnotationName {
-  optional uint64 iid = 1;
-  optional string name = 2;
-}
-
-// End of protos/perfetto/trace/track_event/debug_annotation.proto
-
-// Begin of protos/perfetto/trace/track_event/log_message.proto
-
-message LogMessage {
-  optional uint64 source_location_iid = 1;  // interned SourceLocation.
-  optional uint64 body_iid = 2;             // interned LogMessageBody.
-}
-
-// --------------------
-// Interned data types:
-// --------------------
-
-message LogMessageBody {
-  optional uint64 iid = 1;
-  optional string body = 2;
-}
-// End of protos/perfetto/trace/track_event/log_message.proto
-
-// Begin of protos/perfetto/trace/track_event/process_descriptor.proto
-
-// Describes a process's attributes. Emitted as part of a TrackDescriptor,
-// usually by the process's main thread.
-//
-// Next id: 7.
-message ProcessDescriptor {
-  optional int32 pid = 1;
-  repeated string cmdline = 2;
-  optional string process_name = 6;
-
-  optional int32 process_priority = 5;
-
-  // ---------------------------------------------------------------------------
-  // Deprecated / legacy fields, which will be removed in the future:
-  // ---------------------------------------------------------------------------
-
-  // See chromium's content::ProcessType.
-  enum ChromeProcessType {
-    PROCESS_UNSPECIFIED = 0;
-    PROCESS_BROWSER = 1;
-    PROCESS_RENDERER = 2;
-    PROCESS_UTILITY = 3;
-    PROCESS_ZYGOTE = 4;
-    PROCESS_SANDBOX_HELPER = 5;
-    PROCESS_GPU = 6;
-    PROCESS_PPAPI_PLUGIN = 7;
-    PROCESS_PPAPI_BROKER = 8;
-  }
-  optional ChromeProcessType chrome_process_type = 4;
-
-  // To support old UI. New UI should determine default sorting by process_type.
-  optional int32 legacy_sort_index = 3;
-}
-
-// End of protos/perfetto/trace/track_event/process_descriptor.proto
-
-// Begin of protos/perfetto/trace/track_event/source_location.proto
-
-// --------------------
-// Interned data types:
-// --------------------
-
-message SourceLocation {
-  optional uint64 iid = 1;
-
-  // We intend to add a binary symbol version of this in the future.
-  optional string file_name = 2;
-  optional string function_name = 3;
-  optional uint32 line_number = 4;
-}
-
-// End of protos/perfetto/trace/track_event/source_location.proto
-
 // Begin of protos/perfetto/trace/track_event/task_execution.proto
 
 // TrackEvent arguments describing the execution of a task.
@@ -4292,123 +5369,6 @@
 }
 // End of protos/perfetto/trace/track_event/task_execution.proto
 
-// Begin of protos/perfetto/trace/track_event/thread_descriptor.proto
-
-// Describes a thread's attributes. Emitted as part of a TrackDescriptor,
-// usually by the thread's trace writer.
-//
-// Next id: 9.
-message ThreadDescriptor {
-  optional int32 pid = 1;
-  optional int32 tid = 2;
-
-  optional string thread_name = 5;
-
-  // ---------------------------------------------------------------------------
-  // Deprecated / legacy fields, which will be removed in the future:
-  // ---------------------------------------------------------------------------
-
-  enum ChromeThreadType {
-    CHROME_THREAD_UNSPECIFIED = 0;
-
-    CHROME_THREAD_MAIN = 1;
-    CHROME_THREAD_IO = 2;
-
-    // Scheduler:
-    CHROME_THREAD_POOL_BG_WORKER = 3;
-    CHROME_THREAD_POOL_FG_WORKER = 4;
-    CHROME_THREAD_POOL_FB_BLOCKING = 5;
-    CHROME_THREAD_POOL_BG_BLOCKING = 6;
-    CHROME_THREAD_POOL_SERVICE = 7;
-
-    // Compositor:
-    CHROME_THREAD_COMPOSITOR = 8;
-    CHROME_THREAD_VIZ_COMPOSITOR = 9;
-    CHROME_THREAD_COMPOSITOR_WORKER = 10;
-
-    // Renderer:
-    CHROME_THREAD_SERVICE_WORKER = 11;
-
-    // Tracing related threads:
-    CHROME_THREAD_MEMORY_INFRA = 50;
-    CHROME_THREAD_SAMPLING_PROFILER = 51;
-  };
-  optional ChromeThreadType chrome_thread_type = 4;
-
-  // Deprecated. Use ClockSnapshot in combination with TracePacket's timestamp
-  // and timestamp_clock_id fields instead.
-  optional int64 reference_timestamp_us = 6;
-
-  // Absolute reference values. Clock values in subsequent TrackEvents can be
-  // encoded accumulatively and relative to these. This reduces their var-int
-  // encoding size.
-  // TODO(eseckler): Deprecated. Replace these with ClockSnapshot encoding.
-  optional int64 reference_thread_time_us = 7;
-  optional int64 reference_thread_instruction_count = 8;
-
-  // To support old UI. New UI should determine default sorting by thread_type.
-  optional int32 legacy_sort_index = 3;
-}
-
-// End of protos/perfetto/trace/track_event/thread_descriptor.proto
-
-// 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.
-//
-// 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
-// when the ring buffer wraps, it should be reemitted whenever incremental state
-// is cleared.
-//
-// As a fallback, TrackEvents emitted without an explicit track association will
-// be associated with an implicit trace-global track (uuid = 0), see also
-// |TrackEvent::track_uuid|. It is possible but not necessary to emit a
-// TrackDescriptor for this implicit track.
-//
-// Next id: 8.
-message TrackDescriptor {
-  // Unique ID that identifies this track. This ID is global to the whole trace.
-  // Producers should ensure that it is unlikely to clash with IDs emitted by
-  // other producers. A value of 0 denotes the implicit trace-global track.
-  //
-  // For example, legacy TRACE_EVENT macros may use a hash involving the async
-  // event id + id_scope, pid, and/or tid to compute this ID.
-  optional uint64 uuid = 1;
-
-  // A parent track reference can be used to describe relationships between
-  // tracks. For example, to define an asynchronous track which is scoped to a
-  // specific process, specify the uuid for that process's process track here.
-  optional uint64 parent_uuid = 5;
-
-  // Name of the track.
-  optional string name = 2;
-
-  // 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;
-}
-
-// End of protos/perfetto/trace/track_event/track_descriptor.proto
-
 // Begin of protos/perfetto/trace/track_event/track_event.proto
 
 // NOTE: Full TrackEvent support in the client lib and chrome is WIP, thus these
@@ -4470,7 +5430,7 @@
 // their default track association) can be emitted as part of a
 // TrackEventDefaults message.
 //
-// Next reserved id: 12 (up to 15). Next id: 29.
+// Next reserved id: 13 (up to 15). Next id: 32.
 message TrackEvent {
   // Names of categories of the event. In the client library, categories are a
   // way to turn groups of individual events on or off.
@@ -4517,7 +5477,11 @@
     // of slice events on the same track.
     TYPE_INSTANT = 3;
 
-    // TODO(eseckler): Add support for counters.
+    // Event that provides a value for a counter track. |track_uuid| should
+    // refer to a counter track and |counter_value| set to the new value. Note
+    // that most other TrackEvent fields (e.g. categories, name, ..) are not
+    // supported for TYPE_COUNTER events. See also CounterDescriptor.
+    TYPE_COUNTER = 4;
   }
   optional Type type = 9;
 
@@ -4528,32 +5492,33 @@
   // implicit trace-global track (uuid 0). See TrackDescriptor::uuid.
   optional uint64 track_uuid = 11;
 
+  // A new value for a counter track. |track_uuid| should refer to a track with
+  // a CounterDescriptor, and |type| should be TYPE_COUNTER. For a more
+  // efficient encoding of counter values that are sampled at the beginning/end
+  // of a slice, see |extra_counter_values| and |extra_counter_track_uuids|.
+  // Counter values can optionally be encoded in as delta values (positive or
+  // negative) on each packet sequence (see CounterIncrementalBase).
+  optional int64 counter_value = 30;
+
+  // To encode counter values more efficiently, we support attaching additional
+  // counter values to a TrackEvent of any type. All values will share the same
+  // timestamp specified in the TracePacket. The value at
+  // extra_counter_values[N] is for the counter track referenced by
+  // extra_counter_track_uuids[N].
+  //
+  // |extra_counter_track_uuids| may also be set via TrackEventDefaults. There
+  // should always be equal or more uuids than values. It is valid to set more
+  // uuids (e.g. via defaults) than values. If uuids are specified in
+  // TrackEventDefaults and a TrackEvent, the TrackEvent uuids override the
+  // default uuid list.
+  //
+  // For example, this allows snapshotting the thread time clock at each
+  // thread-track BEGIN and END event to capture the cpu time delta of a slice.
+  repeated uint64 extra_counter_track_uuids = 31;
+  repeated int64 extra_counter_values = 12;
+
   // TODO(eseckler): Add flow event support.
 
-  // TODO(eseckler): Encode thread_time and thread_instruction_count using a
-  // ClockSnapshot + clock id instead of ThreadDescriptor's reference values.
-
-  // CPU time for the current thread (e.g., CLOCK_THREAD_CPUTIME_ID) in
-  // microseconds.
-  oneof thread_time {
-    // Delta timestamp value since the last TrackEvent or ThreadDescriptor. To
-    // calculate the absolute timestamp value, sum up all delta values of the
-    // preceding TrackEvents since the last ThreadDescriptor and add the sum to
-    // the |reference_timestamp| in ThreadDescriptor. This value should always
-    // be positive.
-    int64 thread_time_delta_us = 2;
-    // This is a one-off absolute value that does not affect delta timestamp
-    // computation in subsequent TrackEvents.
-    int64 thread_time_absolute_us = 17;
-  }
-
-  // Value of the instruction counter for the current thread.
-  oneof thread_instruction_count {
-    // Same encoding as |thread_time| field above.
-    int64 thread_instruction_count_delta = 8;
-    int64 thread_instruction_count_absolute = 20;
-  }
-
   // ---------------------------------------------------------------------------
   // TrackEvent arguments:
   // ---------------------------------------------------------------------------
@@ -4570,6 +5535,7 @@
   optional ChromeKeyedService chrome_keyed_service = 26;
   optional ChromeLegacyIpc chrome_legacy_ipc = 27;
   optional ChromeHistogramSample chrome_histogram_sample = 28;
+  optional ChromeLatencyInfo chrome_latency_info = 29;
   // New argument types go here :)
 
   // ---------------------------------------------------------------------------
@@ -4593,6 +5559,33 @@
     int64 timestamp_absolute_us = 16;
   }
 
+  // Deprecated. Use |extra_counter_values| and |extra_counter_track_uuids| to
+  // encode thread time instead.
+  //
+  // CPU time for the current thread (e.g., CLOCK_THREAD_CPUTIME_ID) in
+  // microseconds.
+  oneof thread_time {
+    // Delta timestamp value since the last TrackEvent or ThreadDescriptor. To
+    // calculate the absolute timestamp value, sum up all delta values of the
+    // preceding TrackEvents since the last ThreadDescriptor and add the sum to
+    // the |reference_timestamp| in ThreadDescriptor. This value should always
+    // be positive.
+    int64 thread_time_delta_us = 2;
+    // This is a one-off absolute value that does not affect delta timestamp
+    // computation in subsequent TrackEvents.
+    int64 thread_time_absolute_us = 17;
+  }
+
+  // Deprecated. Use |extra_counter_values| and |extra_counter_track_uuids| to
+  // encode thread instruction count instead.
+  //
+  // Value of the instruction counter for the current thread.
+  oneof thread_instruction_count {
+    // Same encoding as |thread_time| field above.
+    int64 thread_instruction_count_delta = 8;
+    int64 thread_instruction_count_absolute = 20;
+  }
+
   // Apart from {category, time, thread time, tid, pid}, other legacy trace
   // event attributes are initially simply proxied for conversion to a JSON
   // trace. We intend to gradually transition these attributes to similar native
@@ -4663,6 +5656,7 @@
 // corresponding fields in TrackEvent.
 message TrackEventDefaults {
   optional uint64 track_uuid = 11;
+  repeated uint64 extra_counter_track_uuids = 31;
 
   // TODO(eseckler): Support default values for more TrackEvent fields.
 }
@@ -4683,6 +5677,1066 @@
 
 // End of protos/perfetto/trace/track_event/track_event.proto
 
+// Begin of protos/perfetto/trace/interned_data/interned_data.proto
+
+// ------------------------------ DATA INTERNING: ------------------------------
+// Interning indexes are built up gradually by adding the entries contained in
+// each TracePacket of the same packet sequence (packets emitted by the same
+// producer and TraceWriter, see |trusted_packet_sequence_id|). Thus, packets
+// can only refer to interned data from other packets in the same sequence.
+//
+// The writer will emit new entries when it encounters new internable values
+// that aren't yet in the index. Data in current and subsequent TracePackets can
+// then refer to the entry by its position (interning ID, abbreviated "iid") in
+// its index. An interning ID with value 0 is considered invalid (not set).
+//
+// Because of the incremental build-up, the interning index will miss data when
+// TracePackets are lost, e.g. because a chunk was overridden in the central
+// ring buffer. To avoid invalidation of the whole trace in such a case, the
+// index is periodically reset (see SEQ_INCREMENTAL_STATE_CLEARED).
+// When packet loss occurs, the reader will only lose interning data up to the
+// next reset.
+// -----------------------------------------------------------------------------
+
+// Message that contains new entries for the interning indices of a packet
+// sequence.
+//
+// The writer will usually emit new entries in the same TracePacket that first
+// refers to them (since the last reset of interning state). They may also be
+// emitted proactively in advance of referring to them in later packets.
+//
+// Next reserved id: 8 (up to 15).
+// Next id: 23.
+message InternedData {
+  // TODO(eseckler): Replace iid fields inside interned messages with
+  // map<iid, message> type fields in InternedData.
+
+  // Each field's message type needs to specify an |iid| field, which is the ID
+  // of the entry in the field's interning index. Each field constructs its own
+  // index, thus interning IDs are scoped to the tracing session and field
+  // (usually as a counter for efficient var-int encoding). It is illegal to
+  // override entries in an index (using the same iid for two different values)
+  // within the same tracing session, even after a reset of the emitted
+  // interning state.
+  repeated EventCategory event_categories = 1;
+  repeated EventName event_names = 2;
+  repeated DebugAnnotationName debug_annotation_names = 3;
+  repeated SourceLocation source_locations = 4;
+  repeated LogMessageBody log_message_body = 20;
+
+  // Note: field IDs up to 15 should be used for frequent data only.
+
+  // Build IDs of exectuable files.
+  repeated InternedString build_ids = 16;
+  // Paths to executable files.
+  repeated InternedString mapping_paths = 17;
+  // Paths to source files.
+  repeated InternedString source_paths = 18;
+  // Names of functions used in frames below.
+  repeated InternedString function_names = 5;
+  // Symbols that were added to this trace after the fact.
+  repeated ProfiledFrameSymbols profiled_frame_symbols = 21;
+
+  // Executable files mapped into processes.
+  repeated Mapping mappings = 19;
+  // Frames of callstacks of a program.
+  repeated Frame frames = 6;
+  // A callstack of a program.
+  repeated Callstack callstacks = 7;
+
+  // Additional Vulkan information sent in a VulkanMemoryEvent message
+  repeated InternedString vulkan_memory_keys = 22;
+}
+
+// End of protos/perfetto/trace/interned_data/interned_data.proto
+
+// Begin of protos/perfetto/trace/perfetto/perfetto_metatrace.proto
+
+// Used to trace the execution of perfetto itself.
+message PerfettoMetatrace {
+  // See base/metatrace_events.h for definitions.
+  oneof record_type {
+    uint32 event_id = 1;
+    uint32 counter_id = 2;
+  }
+
+  // Only when using |event_id|.
+  optional uint32 event_duration_ns = 3;
+
+  // Only when using |counter_id|.
+  optional int32 counter_value = 4;
+
+  // ID of the thread that emitted the event.
+  optional uint32 thread_id = 5;
+
+  // If true the meta-tracing ring buffer had overruns and hence some data is
+  // missing from this point.
+  optional bool has_overruns = 6;
+}
+
+// End of protos/perfetto/trace/perfetto/perfetto_metatrace.proto
+
+// Begin of protos/perfetto/trace/perfetto/tracing_service_event.proto
+
+// Events emitted by the tracing service when data source states change.
+message TracingServiceEvent {
+  oneof event_type {
+    // When this is set to true, reports the point in time (the TracePacket's
+    // timestamp) when all data sources did see the start event and ACKed it.
+    // This identifies the point in time when it's safe to assume tha all data
+    // sources have been recording events.
+    // Note: like any other event in the trace, the byte offset at which this
+    // packet shows up in the trace is arbitrary and doesn't reflect causal
+    // ordering (i.e. it's okay to use this event to reason about the timestamps
+    // of othe packets, NOT their relative order w.r.t. this packet).
+    bool all_data_sources_started = 1;
+  }
+}
+
+// End of protos/perfetto/trace/perfetto/tracing_service_event.proto
+
+// Begin of protos/perfetto/trace/power/battery_counters.proto
+
+message BatteryCounters {
+  // Battery capacity in microampere-hours(µAh). Also known as Coulomb counter.
+  optional int64 charge_counter_uah = 1;
+
+  // Remaining battery capacity percentage of total capacity
+  optional float capacity_percent = 2;
+
+  // Instantaneous battery current in microamperes(µA).
+  // Positive values indicate net current entering the battery from a charge
+  // source, negative values indicate net current discharging from the battery.
+  optional int64 current_ua = 3;
+
+  // Instantaneous battery current in microamperes(µA).
+  optional int64 current_avg_ua = 4;
+}
+
+// End of protos/perfetto/trace/power/battery_counters.proto
+
+// Begin of protos/perfetto/trace/power/power_rails.proto
+
+message PowerRails {
+
+  message RailDescriptor {
+    // Index corresponding to the rail
+    optional uint32 index = 1;
+
+    // Name of the rail
+    optional string rail_name = 2;
+
+    // Name of the subsystem to which this rail belongs
+    optional string subsys_name = 3;
+
+    // Hardware sampling rate (Hz).
+    optional uint32 sampling_rate = 4;
+  }
+
+  // This is only emitted at the beginning of the trace.
+  repeated RailDescriptor rail_descriptor = 1;
+
+  message EnergyData {
+    // Index corresponding to RailDescriptor.index
+    optional uint32 index = 1;
+
+    // Time since device boot(CLOCK_BOOTTIME) in milli-seconds.
+    optional uint64 timestamp_ms = 2;
+
+    // Accumulated energy since device boot in microwatt-seconds (uWs).
+    optional uint64 energy = 3;
+  }
+
+  repeated EnergyData energy_data = 2;
+}
+
+// End of protos/perfetto/trace/power/power_rails.proto
+
+// Begin of protos/perfetto/trace/profiling/heap_graph.proto
+
+message ObfuscatedMember {
+  // This is the obfuscated field name relative to the class containing the
+  // ObfuscatedMember.
+  optional string obfuscated_name = 1;
+  // If this is fully qualified (i.e. contains a '.') this is the deobfuscated
+  // field name including its class. Otherwise, this is this the unqualified
+  // deobfuscated field name relative to the class containing this
+  // ObfuscatedMember.
+  optional string deobfuscated_name = 2;
+}
+
+message ObfuscatedClass {
+  optional string obfuscated_name = 1;
+  optional string deobfuscated_name = 2;
+  repeated ObfuscatedMember obfuscated_members = 3;
+}
+
+message DeobfuscationMapping {
+  optional string package_name = 1;
+  optional int64 version_code = 2;
+  repeated ObfuscatedClass obfuscated_classes = 3;
+}
+
+message HeapGraphRoot {
+  enum Type {
+    ROOT_UNKNOWN = 0;
+    ROOT_JNI_GLOBAL = 1;
+    ROOT_JNI_LOCAL = 2;
+    ROOT_JAVA_FRAME = 3;
+    ROOT_NATIVE_STACK = 4;
+    ROOT_STICKY_CLASS = 5;
+    ROOT_THREAD_BLOCK = 6;
+    ROOT_MONITOR_USED = 7;
+    ROOT_THREAD_OBJECT = 8;
+    ROOT_INTERNED_STRING = 9;
+    ROOT_FINALIZING = 10;
+    ROOT_DEBUGGER = 11;
+    ROOT_REFERENCE_CLEANUP = 12;
+    ROOT_VM_INTERNAL = 13;
+    ROOT_JNI_MONITOR = 14;
+  };
+  // Objects retained by this root.
+  repeated uint64 object_ids = 1 [packed = true];
+
+  optional Type root_type = 2;
+}
+
+message HeapGraphType {
+  // TODO(fmayer): Consider removing this and using the index in the repeaed
+  // field to save space.
+  optional uint64 id = 1;
+  optional uint64 location_id = 2;
+  optional string class_name = 3;
+}
+
+message HeapGraphObject {
+  optional uint64 id = 1;
+
+  // Index for InternedData.type_names for the name of the type of this object.
+  optional uint64 type_id = 2;
+
+  // Bytes occupied by this objects.
+  optional uint64 self_size = 3;
+
+  // Indices for InternedData.field_names for the name of the field referring
+  // to the object.
+  repeated uint64 reference_field_id = 4 [packed = true];
+
+  // Ids of the Object that is referred to.
+  repeated uint64 reference_object_id = 5 [packed = true];
+}
+
+message HeapGraph {
+  optional int32 pid = 1;
+
+  // This contains all objects at the time this dump was taken. Some of these
+  // will be live, some of those unreachable (garbage). To find the live
+  // objects, the client needs to build the transitive closure of objects
+  // reachable from |roots|.
+  // All objects not contained within that transitive closure are garbage that
+  // has not yet been collected.
+  repeated HeapGraphObject objects = 2;
+
+  // Roots at the time this dump was taken.
+  // All live objects are reachable from the roots. All other objects are
+  // garbage.
+  repeated HeapGraphRoot roots = 7;
+
+  // Types used in HeapGraphObjects.
+  repeated HeapGraphType types = 9;
+
+  // Legacy way to encode types. Names here are emitted by old perfetto_hprof,
+  // which do not include the class_name. Handle like a HeapGraphType with
+  // no location_id.
+  // TODO(b/153552977): Remove this. This was was not used in any publicly
+  // available release.
+  repeated InternedString type_names = 3;
+
+  // Field names for references in managed heap graph.
+  repeated InternedString field_names = 4;
+
+  // Paths of files used in managed heap graph.
+  repeated InternedString location_names = 8;
+
+  optional bool continued = 5;
+  optional uint64 index = 6;
+}
+
+// End of protos/perfetto/trace/profiling/heap_graph.proto
+
+// Begin of protos/perfetto/trace/profiling/profile_packet.proto
+
+message ProfilePacket {
+  // The following interning tables are only used in Android version Q.
+  // In newer versions, these tables are in InternedData
+  // (see protos/perfetto/trace/interned_data) and are shared across
+  // multiple ProfilePackets.
+  // For backwards compatibility, consumers need to first look up interned
+  // data in the tables within the ProfilePacket, and then, if they are empty,
+  // look up in the InternedData instead.
+  repeated InternedString strings = 1;
+  repeated Mapping mappings = 4;
+  repeated Frame frames = 2;
+  repeated Callstack callstacks = 3;
+
+  // Next ID: 9
+  message HeapSample {
+    optional uint64 callstack_id = 1;
+    // bytes allocated at this callstack.
+    optional uint64 self_allocated = 2;
+    // bytes allocated at this callstack that have been freed.
+    optional uint64 self_freed = 3;
+    // bytes allocated at this callstack but not used since the last
+    // dump.
+    // See documentation of idle_allocations in HeapprofdConfig for more
+    // details.
+    optional uint64 self_idle = 7;
+    // Bytes allocated by this callstack but not freed at the time the malloc
+    // heap usage of this process was maximal. This is only set if dump_at_max
+    // is true in HeapprofdConfig. In that case, self_allocated, self_freed and
+    // self_idle will not be set.
+    optional uint64 self_max = 8;
+    optional uint64 timestamp = 4;  // timestamp [opt]
+    // Number of allocations that were sampled at this callstack.
+    optional uint64 alloc_count = 5;
+    // Number of allocations that were sampled at this callstack that have been
+    // freed.
+    optional uint64 free_count = 6;
+  }
+
+  message Histogram {
+    message Bucket {
+      // This bucket counts values from the previous bucket's (or -infinity if
+      // this is the first bucket) upper_limit (inclusive) to this upper_limit
+      // (exclusive).
+      optional uint64 upper_limit = 1;
+      // This is the highest bucket. This is set instead of the upper_limit. Any
+      // values larger or equal to the previous bucket's upper_limit are counted
+      // in this bucket.
+      optional bool max_bucket = 2;
+      // Number of values that fall into this range.
+      optional uint64 count = 3;
+    }
+    repeated Bucket buckets = 1;
+  }
+
+  message ProcessStats {
+    optional uint64 unwinding_errors = 1;
+    optional uint64 heap_samples = 2;
+    optional uint64 map_reparses = 3;
+    optional Histogram unwinding_time_us = 4;
+    optional uint64 total_unwinding_time_us = 5;
+  }
+
+  repeated ProcessHeapSamples process_dumps = 5;
+  message ProcessHeapSamples {
+    optional uint64 pid = 1;
+
+    // This process was profiled from startup.
+    // If false, this process was already running when profiling started.
+    optional bool from_startup = 3;
+
+    // This process was not profiled because a concurrent session was active.
+    // If this is true, samples will be empty.
+    optional bool rejected_concurrent = 4;
+
+    // This process disconnected while it was profiled.
+    // If false, the process outlived the profiling session.
+    optional bool disconnected = 6;
+
+    // If disconnected, this disconnect was caused by the client overrunning
+    // the buffer.
+    optional bool buffer_overran = 7;
+
+    // If disconnected, this disconnected was caused by the shared memory
+    // buffer being corrupted. THIS IS ALWAYS A BUG IN HEAPPROFD OR CLIENT
+    // MEMORY CORRUPTION.
+    optional bool buffer_corrupted = 8;
+
+    // If disconnected, this disconnect was caused by heapprofd exceeding
+    // guardrails during this profiling session.
+    optional bool hit_guardrail = 10;
+
+    // Timestamp of the state of the target process that this dump represents.
+    // This can be different to the timestamp of the TracePackets for various
+    // reasons:
+    // * If disconnected is set above, this is the timestamp of last state
+    //   heapprofd had of the process before it disconnected.
+    // * Otherwise, if the rate of events produced by the process is high,
+    //   heapprofd might be behind.
+    //
+    // TODO(fmayer): This is MONOTONIC_COARSE. Refactor ClockSnapshot::Clock
+    //               to have a type enum that we can reuse here.
+    optional uint64 timestamp = 9;
+
+    // Metadata about heapprofd.
+    optional ProcessStats stats = 5;
+
+    repeated HeapSample samples = 2;
+  }
+
+  // If this is true, the next ProfilePacket in this package_sequence_id is a
+  // continuation of this one.
+  // To get all samples for a process, accummulate its
+  // ProcessHeapSamples.samples until you see continued=false.
+  optional bool continued = 6;
+
+  // Index of this ProfilePacket on its package_sequence_id. Can be used
+  // to detect dropped data.
+  // Verify these are consecutive.
+  optional uint64 index = 7;
+}
+
+// Message used to represent individual stack samples sampled at discrete
+// points in time, rather than aggregated over an interval.
+message StreamingProfilePacket {
+  repeated uint64 callstack_iid = 1;  // Index into InternedData.callstacks
+  // TODO(eseckler): ThreadDescriptor-based timestamps are deprecated. Replace
+  // this with ClockSnapshot-based delta encoding instead.
+  repeated int64 timestamp_delta_us = 2;
+  optional int32 process_priority = 3;
+}
+
+// Namespace for the contained enums.
+message Profiling {
+  enum CpuMode {
+    MODE_UNKNOWN = 0;
+    MODE_KERNEL = 1;
+    MODE_USER = 2;
+    // The following values aren't expected, but included for completeness:
+    MODE_HYPERVISOR = 3;
+    MODE_GUEST_KERNEL = 4;
+    MODE_GUEST_USER = 5;
+  }
+
+  // Enumeration of libunwindstack's error codes.
+  // NB: the integral representations of the two enums are different.
+  enum StackUnwindError {
+    UNWIND_ERROR_UNKNOWN = 0;
+    UNWIND_ERROR_NONE = 1;
+    UNWIND_ERROR_MEMORY_INVALID = 2;
+    UNWIND_ERROR_UNWIND_INFO = 3;
+    UNWIND_ERROR_UNSUPPORTED = 4;
+    UNWIND_ERROR_INVALID_MAP = 5;
+    UNWIND_ERROR_MAX_FRAMES_EXCEEDED = 6;
+    UNWIND_ERROR_REPEATED_FRAME = 7;
+    UNWIND_ERROR_INVALID_ELF = 8;
+  }
+}
+
+// Individual performance sampling packet payload. Typically corresponds to a
+// stack sample on a configration-dependent counter overflow.
+// Timestamps are within the root packet (in the CLOCK_BOOTTIME domain).
+// There are three distinct views of this message:
+// * completely processed sample (callstack_iid set)
+// * indication of kernel buffer data loss (kernel_records_lost set)
+// * indication of skipped samples (sample_skipped_reason set)
+message PerfSample {
+  optional uint32 cpu = 1;
+  optional uint32 pid = 2;
+  optional uint32 tid = 3;
+
+  // Execution state that the process was sampled at.
+  optional Profiling.CpuMode cpu_mode = 5;
+
+  // Unwound callstack. Might be partial, in which case a synthetic "error"
+  // frame is appended, and |unwind_error| is set appropriately.
+  optional uint64 callstack_iid = 4;
+
+  // If set, stack unwinding was incomplete due to an error.
+  // Unset values should be treated as UNWIND_ERROR_NONE.
+  oneof optional_unwind_error { Profiling.StackUnwindError unwind_error = 16; };
+
+  // If set, indicates that this message is not a sample, but rather an
+  // indication of data loss in the ring buffer allocated for |cpu|. Such data
+  // loss occurs when the kernel has insufficient ring buffer capacity to write
+  // a record (which gets discarded). A record in this context is an individual
+  // ring buffer entry, and counts more than just sample records.
+  //
+  // The |timestamp| of the packet corresponds to the time that the producer
+  // wrote the packet for trace-sorting purposes alone, and should not be
+  // interpreted relative to the sample timestamps. This field is sufficient to
+  // detect that *some* kernel data loss happened within the trace, but not the
+  // specific time bounds of that loss (which would require tracking precedessor
+  // & successor timestamps, which is not deemed necessary at the moment).
+  optional uint64 kernel_records_lost = 17;
+
+  // If set, indicates that the profiler encountered a sample that was relevant,
+  // but was skipped.
+  enum SampleSkipReason {
+    PROFILER_SKIP_UNKNOWN = 0;
+    PROFILER_SKIP_READ_STAGE = 1;
+    PROFILER_SKIP_UNWIND_STAGE = 2;
+    PROFILER_SKIP_UNWIND_ENQUEUE = 3;
+  }
+  oneof optional_sample_skipped_reason {
+    SampleSkipReason sample_skipped_reason = 18;
+  };
+}
+
+// End of protos/perfetto/trace/profiling/profile_packet.proto
+
+// Begin of protos/perfetto/trace/profiling/smaps.proto
+
+message SmapsEntry {
+  optional string path = 1;
+  optional uint64 size_kb = 2;
+  optional uint64 private_dirty_kb = 3;
+  optional uint64 swap_kb = 4;
+};
+
+message SmapsPacket {
+  optional uint32 pid = 1;
+  repeated SmapsEntry entries = 2;
+};
+
+// End of protos/perfetto/trace/profiling/smaps.proto
+
+// Begin of protos/perfetto/trace/ps/process_stats.proto
+
+// Per-process periodically sampled stats. These samples are wrapped in a
+// dedicated message (as opposite to be fields in process_tree.proto) because
+// they are dumped at a different rate than cmdline and thread list.
+// Note: not all of these stats will be present in every ProcessStats message
+// and sometimes processes may be missing . This is because counters are
+// cached to reduce emission of counters which do not change.
+message ProcessStats {
+  // Per-thread periodically sampled stats.
+  // Note: not all of these stats will be present in every message. See the note
+  // for ProcessStats.
+  message Thread {
+    optional int32 tid = 1;
+
+    // Pairs of frequency (represented as an index to CpuInfo frequencies) and
+    // time at that frequency (represented as a number of ticks, see SystemInfo
+    // for the HZ (ticks / second) value to convert this to time).
+    // Read from /proc/tid/time_in_state.
+    repeated uint32 cpu_freq_indices = 2;
+    repeated uint64 cpu_freq_ticks = 3;
+  }
+
+  message Process {
+    optional int32 pid = 1;
+
+    // See /proc/[pid]/status in `man 5 proc` for a description of these fields.
+    optional uint64 vm_size_kb = 2;
+    optional uint64 vm_rss_kb = 3;
+    optional uint64 rss_anon_kb = 4;
+    optional uint64 rss_file_kb = 5;
+    optional uint64 rss_shmem_kb = 6;
+    optional uint64 vm_swap_kb = 7;
+    optional uint64 vm_locked_kb = 8;
+    optional uint64 vm_hwm_kb = 9;
+    // When adding a new field remember to update kProcMemCounterSize in
+    // the trace processor.
+
+    optional int64 oom_score_adj = 10;
+
+    repeated Thread threads = 11;
+  }
+  repeated Process processes = 1;
+
+  // The time at which we finish collecting this batch of samples;
+  // the top-level packet timestamp is the time at which
+  // we begin collection.
+  // TODO(dancol): analysis might be improved by
+  // time-bracketing each sample as well as the whole
+  // ProcessStats, but doing that is probably gated on
+  // a vdso for CLOCK_BOOTTIME.
+  optional uint64 collection_end_timestamp = 2;
+}
+
+// End of protos/perfetto/trace/ps/process_stats.proto
+
+// Begin of protos/perfetto/trace/ps/process_tree.proto
+
+message ProcessTree {
+  // Representation of a thread.
+  message Thread {
+    // The thread id (as per gettid())
+    optional int32 tid = 1;
+
+    // Thread group id (i.e. the PID of the process, == TID of the main thread)
+    optional int32 tgid = 3;
+
+    // The name of the thread.
+    optional string name = 2;
+  }
+
+  // Representation of a process.
+  message Process {
+    // The UNIX process ID, aka thread group ID (as per getpid()).
+    optional int32 pid = 1;
+
+    // The parent process ID, as per getppid().
+    optional int32 ppid = 2;
+
+    // The command line for the process, as per /proc/pid/cmdline.
+    // If it is a kernel thread there will only be one cmdline field
+    // and it will contain /proc/pid/comm.
+    repeated string cmdline = 3;
+
+    // No longer used as of Apr 2018, when the dedicated |threads| field was
+    // introduced in ProcessTree.
+    repeated Thread threads_deprecated = 4 [deprecated = true];
+
+    // The uid for the process, as per /proc/pid/status.
+    optional int32 uid = 5;
+  }
+
+  // List of processes and threads in the client. These lists are incremental
+  // and not exhaustive. A process and its threads might show up separately in
+  // different ProcessTree messages. A thread might event not show up at all, if
+  // no sched_switch activity was detected, for instance:
+  // #0 { processes: [{pid: 10, ...}], threads: [{pid: 11, tgid: 10}] }
+  // #1 { threads: [{pid: 12, tgid: 10}] }
+  // #2 { processes: [{pid: 20, ...}], threads: [{pid: 13, tgid: 10}] }
+  repeated Process processes = 1;
+  repeated Thread threads = 2;
+
+  // The time at which we finish collecting this process tree;
+  // the top-level packet timestamp is the time at which
+  // we begin collection.
+  optional uint64 collection_end_timestamp = 3;
+}
+
+// End of protos/perfetto/trace/ps/process_tree.proto
+
+// Begin of protos/perfetto/trace/sys_stats/sys_stats.proto
+
+// Various Linux system stat counters from /proc.
+// The fields in this message can be reported at different rates and with
+// different granularity. See sys_stats_config.proto.
+message SysStats {
+  // Counters from /proc/meminfo. Values are in KB.
+  message MeminfoValue {
+    optional MeminfoCounters key = 1;
+    optional uint64 value = 2;
+  };
+  repeated MeminfoValue meminfo = 1;
+
+  // Counter from /proc/vmstat. Units are often pages, not KB.
+  message VmstatValue {
+    optional VmstatCounters key = 1;
+    optional uint64 value = 2;
+  };
+  repeated VmstatValue vmstat = 2;
+
+  // Times in each mode, since boot. Unit: nanoseconds.
+  message CpuTimes {
+    optional uint32 cpu_id = 1;
+    optional uint64 user_ns = 2;         // Time spent in user mode.
+    optional uint64 user_ice_ns = 3;     // Time spent in user mode (low prio).
+    optional uint64 system_mode_ns = 4;  // Time spent in system mode.
+    optional uint64 idle_ns = 5;         // Time spent in the idle task.
+    optional uint64 io_wait_ns = 6;      // Time spent waiting for I/O.
+    optional uint64 irq_ns = 7;          // Time spent servicing interrupts.
+    optional uint64 softirq_ns = 8;      // Time spent servicing softirqs.
+  }
+  repeated CpuTimes cpu_stat = 3;  // One entry per cpu.
+
+  // Num processes forked since boot.
+  // Populated only if FORK_COUNT in config.stat_counters.
+  optional uint64 num_forks = 4;
+
+  message InterruptCount {
+    optional int32 irq = 1;
+    optional uint64 count = 2;
+  }
+
+  // Number of interrupts, broken by IRQ number.
+  // Populated only if IRQ_COUNTS in config.stat_counters.
+  optional uint64 num_irq_total = 5;  // Total num of irqs serviced since boot.
+  repeated InterruptCount num_irq = 6;
+
+  // Number of softirqs, broken by softirq number.
+  // Populated only if SOFTIRQ_COUNTS in config.stat_counters.
+  optional uint64 num_softirq_total = 7;    // Total num of softirqs since boot.
+  repeated InterruptCount num_softirq = 8;  // Per-softirq count.
+
+  // The time at which we finish collecting this set of samples;
+  // the top-level packet timestamp is the time at which
+  // we begin collection.
+  optional uint64 collection_end_timestamp = 9;
+}
+
+// End of protos/perfetto/trace/sys_stats/sys_stats.proto
+
+// Begin of protos/perfetto/trace/system_info.proto
+
+message Utsname {
+  optional string sysname = 1;
+  optional string version = 2;
+  optional string release = 3;
+  optional string machine = 4;
+}
+
+message SystemInfo {
+  optional Utsname utsname = 1;
+  optional string android_build_fingerprint = 2;
+
+  // Ticks per second - sysconf(_SC_CLK_TCK).
+  optional int64 hz = 3;
+}
+
+// End of protos/perfetto/trace/system_info.proto
+
+// Begin of protos/perfetto/trace/system_info/cpu_info.proto
+
+// Information about CPUs from procfs and sysfs.
+message CpuInfo {
+  // Information about a single CPU.
+  message Cpu {
+    // Value of "Processor" field from /proc/cpuinfo for this CPU.
+    // Example: "AArch64 Processor rev 12 (aarch64)"
+    optional string processor = 1;
+
+    // Frequencies from
+    // /sys/devices/system/cpu/cpuX/cpufreq/scaling_available_frequencies
+    // where X is the index of this CPU.
+    repeated uint32 frequencies = 2;
+  }
+
+  // Describes available CPUs, one entry per CPU.
+  repeated Cpu cpus = 1;
+}
+
+// End of protos/perfetto/trace/system_info/cpu_info.proto
+
+// Begin of protos/perfetto/trace/test_event.proto
+
+// Event used by testing code.
+message TestEvent {
+  // Arbitrary string used in tests.
+  optional string str = 1;
+
+  // The current value of the random number sequence used in tests.
+  optional uint32 seq_value = 2;
+}
+
+// End of protos/perfetto/trace/test_event.proto
+
+// Begin of protos/perfetto/trace/trace_packet_defaults.proto
+
+// Default values for TracePacket fields that hold for a particular TraceWriter
+// packet sequence. This message contains a subset of the TracePacket fields
+// with matching IDs. When provided, these fields define the default values
+// that should be applied, at import time, to all TracePacket(s) with the same
+// |trusted_packet_sequence_id|, unless otherwise specified in each packet.
+//
+// Should be reemitted whenever incremental state is cleared on the sequence.
+message TracePacketDefaults {
+  optional uint32 timestamp_clock_id = 58;
+
+  // Default values for TrackEvents (e.g. default track).
+  optional TrackEventDefaults track_event_defaults = 11;
+}
+// End of protos/perfetto/trace/trace_packet_defaults.proto
+
+// Begin of protos/perfetto/trace/track_event/process_descriptor.proto
+
+// Describes a process's attributes. Emitted as part of a TrackDescriptor,
+// usually by the process's main thread.
+//
+// Next id: 7.
+message ProcessDescriptor {
+  optional int32 pid = 1;
+  repeated string cmdline = 2;
+  optional string process_name = 6;
+
+  optional int32 process_priority = 5;
+
+  // ---------------------------------------------------------------------------
+  // Deprecated / legacy fields, which will be removed in the future:
+  // ---------------------------------------------------------------------------
+
+  // See chromium's content::ProcessType.
+  enum ChromeProcessType {
+    PROCESS_UNSPECIFIED = 0;
+    PROCESS_BROWSER = 1;
+    PROCESS_RENDERER = 2;
+    PROCESS_UTILITY = 3;
+    PROCESS_ZYGOTE = 4;
+    PROCESS_SANDBOX_HELPER = 5;
+    PROCESS_GPU = 6;
+    PROCESS_PPAPI_PLUGIN = 7;
+    PROCESS_PPAPI_BROKER = 8;
+  }
+  optional ChromeProcessType chrome_process_type = 4;
+
+  // To support old UI. New UI should determine default sorting by process_type.
+  optional int32 legacy_sort_index = 3;
+}
+
+// End of protos/perfetto/trace/track_event/process_descriptor.proto
+
+// Begin of protos/perfetto/trace/track_event/thread_descriptor.proto
+
+// Describes a thread's attributes. Emitted as part of a TrackDescriptor,
+// usually by the thread's trace writer.
+//
+// Next id: 9.
+message ThreadDescriptor {
+  optional int32 pid = 1;
+  optional int32 tid = 2;
+
+  optional string thread_name = 5;
+
+  // ---------------------------------------------------------------------------
+  // Deprecated / legacy fields, which will be removed in the future:
+  // ---------------------------------------------------------------------------
+
+  enum ChromeThreadType {
+    CHROME_THREAD_UNSPECIFIED = 0;
+
+    CHROME_THREAD_MAIN = 1;
+    CHROME_THREAD_IO = 2;
+
+    // Scheduler:
+    CHROME_THREAD_POOL_BG_WORKER = 3;
+    CHROME_THREAD_POOL_FG_WORKER = 4;
+    CHROME_THREAD_POOL_FB_BLOCKING = 5;
+    CHROME_THREAD_POOL_BG_BLOCKING = 6;
+    CHROME_THREAD_POOL_SERVICE = 7;
+
+    // Compositor:
+    CHROME_THREAD_COMPOSITOR = 8;
+    CHROME_THREAD_VIZ_COMPOSITOR = 9;
+    CHROME_THREAD_COMPOSITOR_WORKER = 10;
+
+    // Renderer:
+    CHROME_THREAD_SERVICE_WORKER = 11;
+
+    // Tracing related threads:
+    CHROME_THREAD_MEMORY_INFRA = 50;
+    CHROME_THREAD_SAMPLING_PROFILER = 51;
+  };
+  optional ChromeThreadType chrome_thread_type = 4;
+
+  // Deprecated. Use ClockSnapshot in combination with TracePacket's timestamp
+  // and timestamp_clock_id fields instead.
+  optional int64 reference_timestamp_us = 6;
+
+  // Absolute reference values. Clock values in subsequent TrackEvents can be
+  // encoded accumulatively and relative to these. This reduces their var-int
+  // encoding size.
+  // TODO(eseckler): Deprecated. Replace these with ClockSnapshot encoding.
+  optional int64 reference_thread_time_us = 7;
+  optional int64 reference_thread_instruction_count = 8;
+
+  // To support old UI. New UI should determine default sorting by thread_type.
+  optional int32 legacy_sort_index = 3;
+}
+
+// End of protos/perfetto/trace/track_event/thread_descriptor.proto
+
+// Begin of protos/perfetto/trace/track_event/chrome_process_descriptor.proto
+
+// Describes the attributes for a Chrome process. Must be paired with a
+// ProcessDescriptor in the same TrackDescriptor.
+//
+// Next id: 4.
+message ChromeProcessDescriptor {
+  // See chromium's content::ProcessType.
+  enum ProcessType {
+    PROCESS_UNSPECIFIED = 0;
+    PROCESS_BROWSER = 1;
+    PROCESS_RENDERER = 2;
+    PROCESS_UTILITY = 3;
+    PROCESS_ZYGOTE = 4;
+    PROCESS_SANDBOX_HELPER = 5;
+    PROCESS_GPU = 6;
+    PROCESS_PPAPI_PLUGIN = 7;
+    PROCESS_PPAPI_BROKER = 8;
+  }
+  optional ProcessType process_type = 1;
+  optional int32 process_priority = 2;
+
+  // To support old UI. New UI should determine default sorting by process_type.
+  optional int32 legacy_sort_index = 3;
+}
+
+// End of protos/perfetto/trace/track_event/chrome_process_descriptor.proto
+
+// Begin of protos/perfetto/trace/track_event/chrome_thread_descriptor.proto
+
+// Describes a Chrome thread's attributes. Emitted as part of a TrackDescriptor,
+// usually by the thread's trace writer. Must be paired with a ThreadDescriptor
+// in the same TrackDescriptor.
+//
+// Next id: 3.
+message ChromeThreadDescriptor {
+  enum ThreadType {
+    THREAD_UNSPECIFIED = 0;
+
+    THREAD_MAIN = 1;
+    THREAD_IO = 2;
+
+    // Scheduler:
+    THREAD_POOL_BG_WORKER = 3;
+    THREAD_POOL_FG_WORKER = 4;
+    THREAD_POOL_BG_BLOCKING = 6;
+    THREAD_POOL_FG_BLOCKING = 5;
+    THREAD_POOL_SERVICE = 7;
+
+    // Compositor:
+    THREAD_COMPOSITOR = 8;
+    THREAD_VIZ_COMPOSITOR = 9;
+    THREAD_COMPOSITOR_WORKER = 10;
+
+    // Renderer:
+    THREAD_SERVICE_WORKER = 11;
+
+    // Tracing related threads:
+    THREAD_MEMORY_INFRA = 50;
+    THREAD_SAMPLING_PROFILER = 51;
+  };
+  optional ThreadType thread_type = 1;
+
+  // To support old UI. New UI should determine default sorting by thread_type.
+  optional int32 legacy_sort_index = 2;
+}
+
+// End of protos/perfetto/trace/track_event/chrome_thread_descriptor.proto
+
+// Begin of protos/perfetto/trace/track_event/counter_descriptor.proto
+
+// Defines properties of a counter track, e.g. for built-in counters (thread
+// time, instruction count, ..) or user-specified counters (e.g. memory usage of
+// a specific app component).
+//
+// Counter tracks only support TYPE_COUNTER track events, which specify new
+// values for the counter. For counters that require per-slice values, counter
+// values can instead be provided in a more efficient encoding via TrackEvent's
+// |extra_counter_track_uuids| and |extra_counter_values| fields. However,
+// slice-type events cannot be emitted onto a counter track.
+//
+// Values for counters that are only emitted on a single packet sequence can
+// optionally be delta-encoded, see |is_incremental|.
+//
+// Next id: 6.
+message CounterDescriptor {
+  // Built-in counters, usually with special meaning in the client library,
+  // trace processor, legacy JSON format, or UI. Trace processor will infer a
+  // track name from the enum value if none is provided in TrackDescriptor.
+  enum BuiltinCounterType {
+    COUNTER_UNSPECIFIED = 0;
+
+    // Thread-scoped counters. The thread's track should be specified via
+    // |parent_uuid| in the TrackDescriptor for such a counter.
+    COUNTER_THREAD_TIME_NS = 1;            // implies UNIT_TIME_NS.
+    COUNTER_THREAD_INSTRUCTION_COUNT = 2;  // implies UNIT_COUNT.
+  }
+
+  // Type of the values for the counters - to supply lower granularity units,
+  // see also |unit_multiplier|.
+  enum Unit {
+    UNIT_UNSPECIFIED = 0;
+    UNIT_TIME_NS = 1;
+    UNIT_COUNT = 2;
+    UNIT_SIZE_BYTES = 3;
+    // TODO(eseckler): Support more units as necessary.
+  }
+
+  // For built-in counters (e.g. thread time). Custom user-specified counters
+  // (e.g. those emitted by TRACE_COUNTER macros of the client library)
+  // shouldn't set this, and instead provide a counter name via TrackDescriptor.
+  optional BuiltinCounterType type = 1;
+
+  // Names of categories of the counter (usually for user-specified counters).
+  // In the client library, categories are a way to turn groups of individual
+  // counters (or events) on or off.
+  repeated string categories = 2;
+
+  // Type of the counter's values. Built-in counters imply a value for this
+  // field.
+  optional Unit unit = 3;
+
+  // Multiplication factor of this counter's values, e.g. to supply
+  // COUNTER_THREAD_TIME_NS timestamps in microseconds instead.
+  optional int64 unit_multiplier = 4;
+
+  // Whether values for this counter are provided as delta values. Only
+  // supported for counters that are emitted on a single packet-sequence (e.g.
+  // thread time). Counter values in subsequent packets on the current packet
+  // sequence will be interpreted as delta values from the sequence's most
+  // recent value for the counter. When incremental state is cleared, the
+  // counter value is considered to be reset to 0. Thus, the first value after
+  // incremental state is cleared is effectively an absolute value.
+  optional bool is_incremental = 5;
+
+  // TODO(eseckler): Support arguments describing the counter (?).
+  // repeated DebugAnnotation debug_annotations;
+}
+
+// End of protos/perfetto/trace/track_event/counter_descriptor.proto
+
+// 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.
+//
+// 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
+// when the ring buffer wraps, it should be reemitted whenever incremental state
+// is cleared.
+//
+// As a fallback, TrackEvents emitted without an explicit track association will
+// be associated with an implicit trace-global track (uuid = 0), see also
+// |TrackEvent::track_uuid|. It is possible but not necessary to emit a
+// TrackDescriptor for this implicit track.
+//
+// Next id: 9.
+message TrackDescriptor {
+  // Unique ID that identifies this track. This ID is global to the whole trace.
+  // Producers should ensure that it is unlikely to clash with IDs emitted by
+  // other producers. A value of 0 denotes the implicit trace-global track.
+  //
+  // For example, legacy TRACE_EVENT macros may use a hash involving the async
+  // event id + id_scope, pid, and/or tid to compute this ID.
+  optional uint64 uuid = 1;
+
+  // A parent track reference can be used to describe relationships between
+  // tracks. For example, to define an asynchronous track which is scoped to a
+  // specific process, specify the uuid for that process's process track here.
+  // Similarly, to associate a COUNTER_THREAD_TIME_NS counter track with a
+  // thread, specify the uuid for that thread's thread track here.
+  optional uint64 parent_uuid = 5;
+
+  // Name of the track. Optional - if unspecified, it may be derived from the
+  // process/thread name (process/thread tracks), the first event's name (async
+  // tracks), or counter name (counter tracks).
+  optional string name = 2;
+
+  // 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;
+
+  // Descriptor for a counter track. If set, the track will only support
+  // TYPE_COUNTER TrackEvents (and values provided via TrackEvent's
+  // |extra_counter_values|).
+  optional CounterDescriptor counter = 8;
+}
+
+// End of protos/perfetto/trace/track_event/track_descriptor.proto
+
 // Begin of protos/perfetto/trace/trigger.proto
 
 // When a TracingSession receives a trigger it records the boot time nanoseconds
@@ -4700,962 +6754,192 @@
 
 // End of protos/perfetto/trace/trigger.proto
 
-// Begin of protos/perfetto/trace/gpu/gpu_counter_event.proto
+// Begin of protos/perfetto/trace/trace_packet.proto
 
-message GpuCounterEvent {
-  // The first trace packet of each session should include counter_spec.
-  optional GpuCounterDescriptor counter_descriptor = 1;
-
-  message GpuCounter {
-    // required. Identifier for counter.
-    optional uint32 counter_id = 1;
-    // required. Value of the counter.
-    oneof value {
-      int64 int_value = 2;
-      double double_value = 3;
-    }
-  }
-  repeated GpuCounter counters = 2;
-
-  // optional. Identifier for GPU in a multi-gpu device.
-  optional int32 gpu_id = 3;
-}
-
-// End of protos/perfetto/trace/gpu/gpu_counter_event.proto
-
-// Begin of protos/perfetto/trace/gpu/gpu_log.proto
-
-// Message for logging events GPU data producer.
-message GpuLog {
-  enum Severity {
-    LOG_SEVERITY_UNSPECIFIED = 0;
-    LOG_SEVERITY_VERBOSE = 1;
-    LOG_SEVERITY_DEBUG = 2;
-    LOG_SEVERITY_INFO = 3;
-    LOG_SEVERITY_WARNING = 4;
-    LOG_SEVERITY_ERROR = 5;
-  };
-  optional Severity severity = 1;
-
-  optional string tag = 2;
-
-  optional string log_message = 3;
-}
-
-// End of protos/perfetto/trace/gpu/gpu_log.proto
-
-// Begin of protos/perfetto/trace/gpu/gpu_render_stage_event.proto
-
-// next id: 13
-message GpuRenderStageEvent {
-  // required. Unique ID for the event.
-  optional uint64 event_id = 1;
-
-  // optional. Duration of the event. This should be in the same clock domain as
-  // the timestamp of the packet. If unset, this is a single time point event.
-  optional uint64 duration = 2;
-
-  // required. ID to a hardware queue description in the specifications.
-  optional int32 hw_queue_id = 3;
-
-  // required. ID to a render stage description in the specifications.
-  optional int32 stage_id = 4;
-
-  // required. GL context/VK device.
-  optional uint64 context = 5;
-
-  // optional. The render target for this event.
-  optional uint64 render_target_handle = 8;
-
-  // optional. The Vulkan render pass handle.
-  optional uint64 render_pass_handle = 9;
-
-  // optional. The Vulkan command buffer handle.
-  optional uint64 command_buffer_handle = 12;
-
-  // optional. Submission ID generated by the UMD. Should map 1:1 with a
-  // vkQueueSubmit.
-  optional uint32 submission_id = 10;
-
-  // optional. Additional data for the user. This may include attribs for
-  // the event like resource ids, shaders etc
-  message ExtraData {
-    optional string name = 1;
-    optional string value = 2;
-  }
-  repeated ExtraData extra_data = 6;
-
-  // The first trace packet of each session should include a Specifications
-  // to enumerate *all* IDs that will be used. The timestamp of this packet
-  // must be earlier than all other packets. Only one packet with Specifications
-  // is expected.
-  message Specifications {
-    message ContextSpec {
-      optional uint64 context = 1;
-      optional int32 pid = 2;
-    }
-    optional ContextSpec context_spec = 1;
-
-    message Description {
-      optional string name = 1;
-      optional string description = 2;
-    }
-
-    // Labels to categorize the hw Queue this event goes on.
-    repeated Description hw_queue = 2;
-
-    // Labels to categorize render stage(binning, render, compute etc).
-    repeated Description stage = 3;
-  }
-  optional Specifications specifications = 7;
-
-  // optional. Identifier for GPU in a multi-gpu device.
-  optional int32 gpu_id = 11;
-
-  // Extension for vendor's custom proto.
-  extensions 100;
-}
-
-// End of protos/perfetto/trace/gpu/gpu_render_stage_event.proto
-
-// Begin of protos/perfetto/trace/gpu/vulkan_api_event.proto
-
-// Message for recording the Vulkan call.
-message VulkanApiEvent {
-  oneof event {
-    VkDebugUtilsObjectName vk_debug_utils_object_name = 1;
-    VkQueueSubmit vk_queue_submit = 2;
-  }
-
-  // For recording vkSetDebugUtilsObjectNameEXT and
-  // vkDebugMarkerSetObjectNameEXT
-  message VkDebugUtilsObjectName {
-    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.
-    optional int32 object_type = 3;
-    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
-
-// Begin of protos/perfetto/config/android/android_log_config.proto
-
-message AndroidLogConfig {
-  repeated AndroidLogId log_ids = 1;
-
-  reserved 2;  // Was |poll_ms|, deprecated.
-
-  // If set ignores all log messages whose prio is < the given value.
-  optional AndroidLogPriority min_prio = 3;
-
-  // If non-empty ignores all log messages whose tag doesn't match one of the
-  // specified values.
-  repeated string filter_tags = 4;
-}
-
-// End of protos/perfetto/config/android/android_log_config.proto
-
-// Begin of protos/perfetto/config/chrome/chrome_config.proto
-
-message ChromeConfig {
-  optional string trace_config = 1;
-
-  // When enabled, the data source should only fill in fields in the output that
-  // are not potentially privacy sensitive.
-  optional bool privacy_filtering_enabled = 2;
-}
-
-// End of protos/perfetto/config/chrome/chrome_config.proto
-
-// Begin of protos/perfetto/config/data_source_config.proto
-
-// The configuration that is passed to each data source when starting tracing.
-message DataSourceConfig {
-  // Data source unique name, e.g., "linux.ftrace". This must match
-  // the name passed by the data source when it registers (see
-  // RegisterDataSource()).
-  optional string name = 1;
-
-  // The index of the logging buffer where TracePacket(s) will be stored.
-  // This field doesn't make a major difference for the Producer(s). The final
-  // logging buffers, in fact, are completely owned by the Service. We just ask
-  // the Producer to copy this number into the chunk headers it emits, so that
-  // the Service can quickly identify the buffer where to move the chunks into
-  // without expensive lookups on its fastpath.
-  optional uint32 target_buffer = 2;
-
-  // Set by the service to indicate the duration of the trace.
-  // DO NOT SET in consumer as this will be overridden by the service.
-  optional uint32 trace_duration_ms = 3;
-
-  // Set by the service to indicate how long it waits after StopDataSource.
-  // DO NOT SET in consumer as this will be overridden by the service.
-  optional uint32 stop_timeout_ms = 7;
-
-  // Set by the service to indicate whether this tracing session has extra
-  // guardrails.
-  // DO NOT SET in consumer as this will be overridden by the service.
-  optional bool enable_extra_guardrails = 6;
-
-  // Set by the service to indicate which tracing session the data source
-  // belongs to. The intended use case for this is checking if two data sources,
-  // one of which produces metadata for the other one, belong to the same trace
-  // session and hence should be linked together.
-  // This field was introduced in Aug 2018 after Android P.
-  optional uint64 tracing_session_id = 4;
-
-  // Keeep the lower IDs (up to 99) for fields that are *not* specific to
-  // data-sources and needs to be processed by the traced daemon.
-
-  // All data source config fields must be marked as [lazy=true]. This prevents
-  // the proto-to-cpp generator from recursing into those when generating the
-  // cpp classes and polluting tracing/core with data-source-specific classes.
-  // Instead they are treated as opaque strings containing raw proto bytes.
-
-  // Data source name: linux.ftrace
-  optional FtraceConfig ftrace_config = 100 [lazy = true];
-  // Data source name: linux.inode_file_map
-  optional InodeFileConfig inode_file_config = 102 [lazy = true];
-  // Data source name: linux.process_stats
-  optional ProcessStatsConfig process_stats_config = 103 [lazy = true];
-  // Data source name: linux.sys_stats
-  optional SysStatsConfig sys_stats_config = 104 [lazy = true];
-  // Data source name: android.heapprofd
-  optional HeapprofdConfig heapprofd_config = 105 [lazy = true];
-  // Data source name: android.java_hprof
-  optional JavaHprofConfig java_hprof_config = 110 [lazy = true];
-  // Data source name: android.power
-  optional AndroidPowerConfig android_power_config = 106 [lazy = true];
-  // Data source name: android.log
-  optional AndroidLogConfig android_log_config = 107 [lazy = true];
-  // TODO(fmayer): Add data source name for this.
-  optional GpuCounterConfig gpu_counter_config = 108 [lazy = true];
-  // Data source name: android.packages_list
-  optional PackagesListConfig packages_list_config = 109 [lazy = true];
-  // Data source name: linux.perf
-  optional PerfEventConfig perf_event_config = 111 [lazy = true];
-  // Data source name: vulkan.memory_tracker
-  optional VulkanMemoryConfig vulkan_memory_config = 112 [lazy = true];
-
-  // Chrome is special as it doesn't use the perfetto IPC layer. We want to
-  // avoid proto serialization and de-serialization there because that would
-  // just add extra hops on top of the Mojo ser/des. Instead we auto-generate a
-  // C++ class for it so it can pass around plain C++ objets.
-  optional ChromeConfig chrome_config = 101;
-
-  // This is a fallback mechanism to send a free-form text config to the
-  // producer. In theory this should never be needed. All the code that
-  // is part of the platform (i.e. traced service) is supposed to *not* truncate
-  // the trace config proto and propagate unknown fields. However, if anything
-  // in the pipeline (client or backend) ends up breaking this forward compat
-  // plan, this field will become the escape hatch to allow future data sources
-  // to get some meaningful configuration.
-  optional string legacy_config = 1000;
-
-  // This field is only used for testing.
-  optional TestConfig for_testing = 1001;
-
-  reserved 268435455;  // Was |for_testing|. Caused more problems then found.
-}
-
-// End of protos/perfetto/config/data_source_config.proto
-
-// Begin of protos/perfetto/config/ftrace/ftrace_config.proto
-
-message FtraceConfig {
-  repeated string ftrace_events = 1;
-  repeated string atrace_categories = 2;
-  repeated string atrace_apps = 3;
-  // *Per-CPU* buffer size.
-  optional uint32 buffer_size_kb = 10;
-  optional uint32 drain_period_ms = 11;
-
-  // Configuration for compact encoding of scheduler events. When enabled (and
-  // recording the relevant ftrace events), specific high-volume events are
-  // encoded in a denser format than normal.
-  message CompactSchedConfig {
-    // If true, and sched_switch or sched_waking ftrace events are enabled,
-    // record those events in the compact format.
-    optional bool enabled = 1;
-  }
-  optional CompactSchedConfig compact_sched = 12;
-}
-
-// End of protos/perfetto/config/ftrace/ftrace_config.proto
-
-// Begin of protos/perfetto/config/inode_file/inode_file_config.proto
-
-message InodeFileConfig {
-  message MountPointMappingEntry {
-    optional string mountpoint = 1;
-    repeated string scan_roots = 2;
-  }
-
-  // How long to pause between batches.
-  optional uint32 scan_interval_ms = 1;
-
-  // How long to wait before the first scan in order to accumulate inodes.
-  optional uint32 scan_delay_ms = 2;
-
-  // How many inodes to scan in one batch.
-  optional uint32 scan_batch_size = 3;
-
-  // Do not scan for inodes not found in the static map.
-  optional bool do_not_scan = 4;
-
-  // If non-empty, only scan inodes corresponding to block devices named in
-  // this list.
-  repeated string scan_mount_points = 5;
-
-  // When encountering an inode belonging to a block device corresponding
-  // to one of the mount points in this map, scan its scan_roots instead.
-  repeated MountPointMappingEntry mount_point_mapping = 6;
-}
-
-// End of protos/perfetto/config/inode_file/inode_file_config.proto
-
-// Begin of protos/perfetto/config/power/android_power_config.proto
-
-message AndroidPowerConfig {
-  enum BatteryCounters {
-    BATTERY_COUNTER_UNSPECIFIED = 0;
-    BATTERY_COUNTER_CHARGE = 1;            // Coulomb counter.
-    BATTERY_COUNTER_CAPACITY_PERCENT = 2;  // Charge (%).
-    BATTERY_COUNTER_CURRENT = 3;           // Instantaneous current.
-    BATTERY_COUNTER_CURRENT_AVG = 4;       // Avg current.
-  }
-  optional uint32 battery_poll_ms = 1;
-  repeated BatteryCounters battery_counters = 2;
-
-  // Where available enables per-power-rail measurements.
-  optional bool collect_power_rails = 3;
-}
-
-// End of protos/perfetto/config/power/android_power_config.proto
-
-// Begin of protos/perfetto/config/process_stats/process_stats_config.proto
-
-message ProcessStatsConfig {
-  enum Quirks {
-    QUIRKS_UNSPECIFIED = 0;
-
-    // This has been deprecated and ignored as per 2018-05-01. Full scan at
-    // startup is now disabled by default and can be re-enabled using the
-    // |scan_all_processes_on_start| arg.
-    DISABLE_INITIAL_DUMP = 1 [deprecated = true];
-
-    DISABLE_ON_DEMAND = 2;
-  }
-
-  repeated Quirks quirks = 1;
-
-  // If enabled all processes will be scanned and dumped when the trace starts.
-  optional bool scan_all_processes_on_start = 2;
-
-  // If enabled thread names are also recoded (this is redundant if sched_switch
-  // is enabled).
-  optional bool record_thread_names = 3;
-
-  // If > 0 samples counters (see process_stats.proto) from
-  // /proc/pid/status and oom_score_adj every X ms.
-  // This is required to be > 100ms to avoid excessive CPU usage.
-  // TODO(primiano): add CPU cost for change this value.
-  optional uint32 proc_stats_poll_ms = 4;
-
-  // If empty samples stats for all processes. If non empty samples stats only
-  // for processes matching the given string in their argv0 (i.e. the first
-  // entry of /proc/pid/cmdline).
-  // TODO(primiano): implement this feature.
-  // repeated string proc_stats_filter = 5;
-
-  // This is required to be either = 0 or a multiple of |proc_stats_poll_ms|
-  // (default: |proc_stats_poll_ms|). If = 0, will be set to
-  // |proc_stats_poll_ms|. Non-multiples will be rounded down to the nearest
-  // multiple.
-  optional uint32 proc_stats_cache_ttl_ms = 6;
-}
-
-// End of protos/perfetto/config/process_stats/process_stats_config.proto
-
-// Begin of protos/perfetto/config/sys_stats/sys_stats_config.proto
-
-// This file defines the configuration for the Linux /proc poller data source,
-// which injects counters in the trace.
-// Counters that are needed in the trace must be explicitly listed in the
-// *_counters fields. This is to avoid spamming the trace with all counters
-// at all times.
-// The sampling rate is configurable. All polling rates (*_period_ms) need
-// to be integer multiples of each other.
-// OK:     [10ms, 10ms, 10ms],  [10ms, 20ms, 10ms],  [10ms, 20ms, 60ms]
-// Not OK: [10ms, 10ms, 11ms],  [10ms, 15ms, 20ms]
-message SysStatsConfig {
-  // Polls /proc/meminfo every X ms, if non-zero.
-  // This is required to be > 10ms to avoid excessive CPU usage.
-  // Cost: 0.3 ms [read] + 0.07 ms [parse + trace injection]
-  optional uint32 meminfo_period_ms = 1;
-
-  // If empty all known counters are reported. Otherwise, only the counters
-  // specified below are reported.
-  repeated MeminfoCounters meminfo_counters = 2;
-
-  // Polls /proc/vmstat every X ms, if non-zero.
-  // This is required to be > 10ms to avoid excessive CPU usage.
-  // Cost: 0.2 ms [read] + 0.3 ms [parse + trace injection]
-  optional uint32 vmstat_period_ms = 3;
-  repeated VmstatCounters vmstat_counters = 4;
-
-  // Pols /proc/stat every X ms, if non-zero.
-  // This is required to be > 10ms to avoid excessive CPU usage.
-  // Cost: 4.1 ms [read] + 1.9 ms [parse + trace injection]
-  optional uint32 stat_period_ms = 5;
-  enum StatCounters {
-    STAT_UNSPECIFIED = 0;
-    STAT_CPU_TIMES = 1;
-    STAT_IRQ_COUNTS = 2;
-    STAT_SOFTIRQ_COUNTS = 3;
-    STAT_FORK_COUNT = 4;
-  }
-  repeated StatCounters stat_counters = 6;
-}
-
-// End of protos/perfetto/config/sys_stats/sys_stats_config.proto
-
-// Begin of protos/perfetto/config/test_config.proto
-
-// The configuration for a fake producer used in tests.
-message TestConfig {
-  message DummyFields {
-    optional uint32 field_uint32 = 1;
-    optional int32 field_int32 = 2;
-    optional uint64 field_uint64 = 3;
-    optional int64 field_int64 = 4;
-    optional fixed64 field_fixed64 = 5;
-    optional sfixed64 field_sfixed64 = 6;
-    optional fixed32 field_fixed32 = 7;
-    optional sfixed32 field_sfixed32 = 8;
-    optional double field_double = 9;
-    optional float field_float = 10;
-    optional sint64 field_sint64 = 11;
-    optional sint32 field_sint32 = 12;
-    optional string field_string = 13;
-    optional bytes field_bytes = 14;
-  }
-
-  // The number of messages the fake producer should send.
-  optional uint32 message_count = 1;
-
-  // The maximum number of messages which should be sent each second.
-  // The actual obserced speed may be lower if the producer is unable to
-  // work fast enough.
-  // If this is zero or unset, the producer will send as fast as possible.
-  optional uint32 max_messages_per_second = 2;
-
-  // The seed value for a simple multiplicative congruential pseudo-random
-  // number sequence.
-  optional uint32 seed = 3;
-
-  // The size of each message in bytes. Should be greater than or equal 5 to
-  // account for the number of bytes needed to encode the random number and a
-  // null byte for the string.
-  optional uint32 message_size = 4;
-
-  // Whether the producer should send a event batch when the data source is
-  // is initially registered.
-  optional bool send_batch_on_register = 5;
-
-  optional DummyFields dummy_fields = 6;
-}
-
-// End of protos/perfetto/config/test_config.proto
-
-// Begin of protos/perfetto/config/trace_config.proto
-
-// The overall config that is used when starting a new tracing session through
-// ProducerPort::StartTracing().
-// It contains the general config for the logging buffer(s) and the configs for
-// all the data source being enabled.
+// The root object emitted by Perfetto. A perfetto trace is just a stream of
+// TracePacket(s).
 //
-// Next id: 29.
-message TraceConfig {
-  message BufferConfig {
-    optional uint32 size_kb = 1;
+// Next reserved id: 13 (up to 15).
+// Next id: 71.
+message TracePacket {
+  // The timestamp of the TracePacket.
+  // By default this timestamps refers to the trace clock (CLOCK_BOOTTIME on
+  // Android). It can be overridden using a different timestamp_clock_id.
+  // The clock domain definition in ClockSnapshot can also override:
+  // - The unit (default: 1ns).
+  // - The absolute vs delta encoding (default: absolute timestamp).
+  optional uint64 timestamp = 8;
 
-    reserved 2;  // |page_size|, now deprecated.
-    reserved 3;  // |optimize_for|, now deprecated.
+  // Specifies the ID of the clock used for the TracePacket |timestamp|. Can be
+  // one of the built-in types from ClockSnapshot::BuiltinClocks, or a
+  // producer-defined clock id.
+  // If unspecified and if no default per-sequence value has been provided via
+  // TracePacketDefaults, it defaults to BuiltinClocks::BOOTTIME.
+  optional uint32 timestamp_clock_id = 58;
 
-    enum FillPolicy {
-      UNSPECIFIED = 0;
+  oneof data {
+    FtraceEventBundle ftrace_events = 1;
+    ProcessTree process_tree = 2;
+    ProcessStats process_stats = 9;
+    InodeFileMap inode_file_map = 4;
+    ChromeEventBundle chrome_events = 5;
+    ClockSnapshot clock_snapshot = 6;
+    SysStats sys_stats = 7;
+    TrackEvent track_event = 11;
 
-      // Default behavior. The buffer operates as a conventional ring buffer.
-      // If the writer is faster than the reader (or if the reader reads only
-      // after tracing is stopped) newly written packets will overwrite old
-      // packets.
-      RING_BUFFER = 1;
+    // IDs up to 15 are reserved. They take only one byte to encode their
+    // preamble so should be used for freqeuent events.
 
-      // Behaves like RING_BUFFER as long as there is space in the buffer or
-      // the reader catches up with the writer. As soon as the writer hits
-      // an unread chunk, it stops accepting new data in the buffer.
-      DISCARD = 2;
-    }
-    optional FillPolicy fill_policy = 4;
-  }
-  repeated BufferConfig buffers = 1;
+    TraceConfig trace_config = 33;
+    FtraceStats ftrace_stats = 34;
+    TraceStats trace_stats = 35;
+    ProfilePacket profile_packet = 37;
+    BatteryCounters battery = 38;
+    PowerRails power_rails = 40;
+    AndroidLogPacket android_log = 39;
+    SystemInfo system_info = 45;
+    Trigger trigger = 46;
+    PackagesList packages_list = 47;
+    ChromeBenchmarkMetadata chrome_benchmark_metadata = 48;
+    PerfettoMetatrace perfetto_metatrace = 49;
+    ChromeMetadataPacket chrome_metadata = 51;
+    GpuCounterEvent gpu_counter_event = 52;
+    GpuRenderStageEvent gpu_render_stage_event = 53;
+    StreamingProfilePacket streaming_profile_packet = 54;
+    HeapGraph heap_graph = 56;
+    GraphicsFrameEvent graphics_frame_event = 57;
+    VulkanMemoryEvent vulkan_memory_event = 62;
+    GpuLog gpu_log = 63;
+    VulkanApiEvent vulkan_api_event = 65;
+    PerfSample perf_sample = 66;
+    CpuInfo cpu_info = 67;
+    SmapsPacket smaps_packet = 68;
+    TracingServiceEvent service_event = 69;
+    InitialDisplayState initial_display_state = 70;
 
-  message DataSource {
-    // Filters and data-source specific config. It contains also the unique name
-    // of the data source, the one passed in the  DataSourceDescriptor when they
-    // register on the service.
-    optional protos.DataSourceConfig config = 1;
+    // Only used in profile packets.
+    ProfiledFrameSymbols profiled_frame_symbols = 55;
+    ModuleSymbols module_symbols = 61;
+    DeobfuscationMapping deobfuscation_mapping = 64;
 
-    // Optional. If multiple producers (~processes) expose the same data source
-    // and |producer_name_filter| != "", the data source is enabled only for
-    // producers whose names match any of the producer_name_filter below.
-    // The |producer_name_filter| has to be an exact match. (TODO(primiano):
-    // support wildcards or regex).
-    // This allows to enable a data source only for specific processes.
-    // The "repeated" field has OR sematics: specifying a filter ["foo", "bar"]
-    // will enable data source on both "foo" and "bar" (if existent).
-    repeated string producer_name_filter = 2;
-  }
-  repeated DataSource data_sources = 2;
+    // Only used by TrackEvent.
+    TrackDescriptor track_descriptor = 60;
 
-  // Config for builtin trace packets emitted by perfetto like trace stats,
-  // system info, etc.
-  message BuiltinDataSource {
-    // Disable emitting clock timestamps into the trace.
-    optional bool disable_clock_snapshotting = 1;
+    // Deprecated, use TrackDescriptor instead.
+    ProcessDescriptor process_descriptor = 43;
 
-    optional bool disable_trace_config = 2;
+    // Deprecated, use TrackDescriptor instead.
+    ThreadDescriptor thread_descriptor = 44;
 
-    optional bool disable_system_info = 3;
-  }
-  optional BuiltinDataSource builtin_data_sources = 20;
+    // This field is emitted at periodic intervals (~10s) and
+    // contains always the binary representation of the UUID
+    // {82477a76-b28d-42ba-81dc-33326d57a079}. This is used to be able to
+    // efficiently partition long traces without having to fully parse them.
+    bytes synchronization_marker = 36;
 
-  // If specified, the trace will be stopped |duration_ms| after starting.
-  // However in case of traces with triggers, see
-  // TriggerConfig.trigger_timeout_ms instead.
-  optional uint32 duration_ms = 3;
+    // Zero or more proto encoded trace packets compressed using deflate.
+    // Each compressed_packets TracePacket (including the two field ids and
+    // sizes) should be less than 512KB.
+    bytes compressed_packets = 50;
 
-  // This is set when --dropbox is passed to the Perfetto command line client
-  // and enables guardrails that limit resource usage for traces requested
-  // by statsd.
-  optional bool enable_extra_guardrails = 4;
-
-  enum LockdownModeOperation {
-    LOCKDOWN_UNCHANGED = 0;
-    LOCKDOWN_CLEAR = 1;
-    LOCKDOWN_SET = 2;
-  }
-  // Reject producers that are not running under the same UID as the tracing
-  // service.
-  optional LockdownModeOperation lockdown_mode = 5;
-
-  message ProducerConfig {
-    // Identifies the producer for which this config is for.
-    optional string producer_name = 1;
-
-    // Specifies the preferred size of the shared memory buffer. If the size is
-    // larger than the max size, the max will be used. If it is smaller than
-    // the page size or doesn't fit pages evenly into it, it will fall back to
-    // the size specified by the producer or finally the default shared memory
-    // size.
-    optional uint32 shm_size_kb = 2;
-
-    // Specifies the preferred size of each page in the shared memory buffer.
-    // Must be an integer multiple of 4K.
-    optional uint32 page_size_kb = 3;
+    // This field is only used for testing.
+    // In previous versions of this proto this field had the id 268435455
+    // This caused many problems:
+    // - protozero decoder does not handle field ids larger than 999.
+    // - old versions of protoc produce Java bindings with syntax errors when
+    //   the field id is large enough.
+    TestEvent for_testing = 900;
   }
 
-  repeated ProducerConfig producers = 6;
-
-  // Contains statsd-specific metadata about an alert associated with the trace.
-  message StatsdMetadata {
-    // The identifier of the alert which triggered this trace.
-    optional int64 triggering_alert_id = 1;
-    // The uid which registered the triggering configuration with statsd.
-    optional int32 triggering_config_uid = 2;
-    // The identifier of the config which triggered the alert.
-    optional int64 triggering_config_id = 3;
-    // The identifier of the subscription which triggered this trace.
-    optional int64 triggering_subscription_id = 4;
-  }
-
-  // Statsd-specific metadata.
-  optional StatsdMetadata statsd_metadata = 7;
-
-  // When true, the EnableTracing() request must also provide a file descriptor.
-  // The service will then periodically read packets out of the trace buffer and
-  // store it into the passed file.
-  optional bool write_into_file = 8;
-
-  // Optional. If non-zero tunes the write period. A min value of 100ms is
-  // enforced (i.e. smaller values are ignored).
-  optional uint32 file_write_period_ms = 9;
-
-  // Optional. When non zero the periodic write stops once at most X bytes
-  // have been written into the file. Tracing is disabled when this limit is
-  // reached, even if |duration_ms| has not been reached yet.
-  optional uint64 max_file_size_bytes = 10;
-
-  // Contains flags which override the default values of the guardrails inside
-  // Perfetto. These values are only affect userdebug builds.
-  message GuardrailOverrides {
-    // Override the default limit (in bytes) for uploading data to server within
-    // a 24 hour period.
-    optional uint64 max_upload_per_day_bytes = 1;
-  }
-
-  optional GuardrailOverrides guardrail_overrides = 11;
-
-  // When true, data sources are not started until an explicit call to
-  // StartTracing() on the consumer port. This is to support early
-  // initialization and fast trace triggering. This can be used only when the
-  // Consumer explicitly triggers the StartTracing() method.
-  // This should not be used in a remote trace config via statsd, doing so will
-  // result in a hung trace session.
-  optional bool deferred_start = 12;
-
-  // When set, it periodically issues a Flush() to all data source, forcing them
-  // to commit their data into the tracing service. This can be used for
-  // quasi-real-time streaming mode and to guarantee some partial ordering of
-  // events in the trace in windows of X ms.
-  optional uint32 flush_period_ms = 13;
-
-  // Wait for this long for producers to acknowledge flush requests.
-  // Default 5s.
-  optional uint32 flush_timeout_ms = 14;
-
-  // Wait for this long for producers to acknowledge stop requests.
-  // Default 5s.
-  optional uint32 data_source_stop_timeout_ms = 23;
-
-  reserved 15;  // |disable_clock_snapshotting| moved.
-
-  // Android-only. If set, sends an intent to the Traceur system app when the
-  // trace ends to notify it about the trace readiness.
-  optional bool notify_traceur = 16;
-
-  // Triggers allow producers to start or stop the tracing session when an event
-  // occurs.
+  // Trusted user id of the producer which generated this packet. Keep in sync
+  // with TrustedPacket.trusted_uid.
   //
-  // For example if we are tracing probabilistically, most traces will be
-  // uninteresting. Triggers allow us to keep only the interesting ones such as
-  // those traces during which the device temperature reached a certain
-  // threshold. In this case the producer can activate a trigger to keep
-  // (STOP_TRACING) the trace, otherwise it can also begin a trace
-  // (START_TRACING) because it knows something is about to happen.
-  message TriggerConfig {
-    enum TriggerMode {
-      UNSPECIFIED = 0;
+  // TODO(eseckler): Emit this field in a PacketSequenceDescriptor message
+  // instead.
+  oneof optional_trusted_uid { int32 trusted_uid = 3; };
 
-      // When this mode is chosen, data sources are not started until one of the
-      // |triggers| are received. This supports early initialization and fast
-      // starting of the tracing system. On triggering, the session will then
-      // record for |stop_delay_ms|. However if no trigger is seen
-      // after |trigger_timeout_ms| the session will be stopped and no data will
-      // be returned.
-      START_TRACING = 1;
-
-      // When this mode is chosen, the session will be started via the normal
-      // EnableTracing() & StartTracing(). If no trigger is ever seen
-      // the session will be stopped after |trigger_timeout_ms| and no data will
-      // be returned. However if triggered the trace will stop after
-      // |stop_delay_ms| and any data in the buffer will be returned to the
-      // consumer.
-      STOP_TRACING = 2;
-    }
-    optional TriggerMode trigger_mode = 1;
-
-    message Trigger {
-      // The producer must specify this name to activate the trigger.
-      optional string name = 1;
-
-      // The a std::regex that will match the producer that can activate this
-      // trigger. This is optional. If unset any producers can activate this
-      // trigger.
-      optional string producer_name_regex = 2;
-
-      // After a trigger is received either in START_TRACING or STOP_TRACING
-      // mode then the trace will end |stop_delay_ms| after triggering.
-      optional uint32 stop_delay_ms = 3;
-    }
-    // A list of triggers which are related to this configuration. If ANY
-    // trigger is seen then an action will be performed based on |trigger_mode|.
-    repeated Trigger triggers = 2;
-
-    // Required and must be positive if a TriggerConfig is specified. This is
-    // how long this TraceConfig should wait for a trigger to arrive. After this
-    // period of time if no trigger is seen the TracingSession will be cleaned
-    // up.
-    optional uint32 trigger_timeout_ms = 3;
+  // Service-assigned identifier of the packet sequence this packet belongs to.
+  // Uniquely identifies a producer + writer pair within the tracing session. A
+  // value of zero denotes an invalid ID. Keep in sync with
+  // TrustedPacket.trusted_packet_sequence_id.
+  oneof optional_trusted_packet_sequence_id {
+    uint32 trusted_packet_sequence_id = 10;
   }
-  optional TriggerConfig trigger_config = 17;
 
-  // When this is non-empty the perfetto command line tool will ignore the rest
-  // of this TraceConfig and instead connect to the perfetto service as a
-  // producer and send these triggers, potentially stopping or starting traces
-  // that were previous configured to use a TriggerConfig.
-  repeated string activate_triggers = 18;
+  // Incrementally emitted interned data, valid only on the packet's sequence
+  // (packets with the same |trusted_packet_sequence_id|). The writer will
+  // usually emit new interned data in the same TracePacket that first refers to
+  // it (since the last reset of interning state). It may also be emitted
+  // proactively in advance of referring to them in later packets.
+  optional InternedData interned_data = 12;
 
-  // Configuration for trace contents that reference earlier trace data. For
-  // example, a data source might intern strings, and emit packets containing
-  // {interned id : string} pairs. Future packets from that data source can then
-  // use the interned ids instead of duplicating the raw string contents. The
-  // trace parser will then need to use that interning table to fully interpret
-  // the rest of the trace.
-  message IncrementalStateConfig {
-    // If nonzero, notify eligible data sources to clear their incremental state
-    // periodically, with the given period. The notification is sent only to
-    // data sources that have |handles_incremental_state_clear| set in their
-    // DataSourceDescriptor. The notification requests that the data source
-    // stops referring to past trace contents. This is particularly useful when
-    // tracing in ring buffer mode, where it is not exceptional to overwrite old
-    // trace data.
+  enum SequenceFlags {
+    SEQ_UNSPECIFIED = 0;
+
+    // Set by the writer to indicate that it will re-emit any incremental data
+    // for the packet's sequence before referring to it again. This includes
+    // interned data as well as periodically emitted data like
+    // Process/ThreadDescriptors. This flag only affects the current packet
+    // sequence (see |trusted_packet_sequence_id|).
     //
-    // Warning: this time-based global clearing is likely to be removed in the
-    // future, to be replaced with a smarter way of sending the notifications
-    // only when necessary.
-    optional uint32 clear_period_ms = 1;
-  }
-  optional IncrementalStateConfig incremental_state_config = 21;
+    // When set, this TracePacket and subsequent TracePackets on the same
+    // sequence will not refer to any incremental data emitted before this
+    // TracePacket. For example, previously emitted interned data will be
+    // re-emitted if it is referred to again.
+    //
+    // When the reader detects packet loss (|previous_packet_dropped|), it needs
+    // to skip packets in the sequence until the next one with this flag set, to
+    // ensure intact incremental data.
+    SEQ_INCREMENTAL_STATE_CLEARED = 1;
 
-  // Additional guardrail used by the Perfetto command line client.
-  // On user builds when --dropbox is set perfetto will refuse to trace unless
-  // this is also set.
-  // Added in Q.
-  optional bool allow_user_build_tracing = 19;
+    // This packet requires incremental state, such as TracePacketDefaults or
+    // InternedData, to be parsed correctly. The trace reader should skip this
+    // packet if incremental state is not valid on this sequence, i.e. if no
+    // packet with the SEQ_INCREMENTAL_STATE_CLEARED flag has been seen on the
+    // current |trusted_packet_sequence_id|.
+    SEQ_NEEDS_INCREMENTAL_STATE = 2;
+  };
+  optional uint32 sequence_flags = 13;
 
-  // If set the tracing service will ensure there is at most one tracing session
-  // with this key.
-  optional string unique_session_name = 22;
+  // DEPRECATED. Moved to SequenceFlags::SEQ_INCREMENTAL_STATE_CLEARED.
+  optional bool incremental_state_cleared = 41;
 
-  // Compress trace with the given method. Best effort.
-  enum CompressionType {
-    COMPRESSION_TYPE_UNSPECIFIED = 0;
-    COMPRESSION_TYPE_DEFLATE = 1;
-  }
-  optional CompressionType compression_type = 24;
+  // Default values for fields of later TracePackets emitted on this packet's
+  // sequence (TracePackets with the same |trusted_packet_sequence_id|).
+  // It must be reemitted when incremental state is cleared (see
+  // |incremental_state_cleared|).
+  // Requires that any future packet emitted on the same sequence specifies
+  // the SEQ_NEEDS_INCREMENTAL_STATE flag.
+  // TracePacketDefaults always override the global defaults for any future
+  // packet on this sequence (regardless of SEQ_NEEDS_INCREMENTAL_STATE).
+  optional TracePacketDefaults trace_packet_defaults = 59;
 
-  // Android-only. Debug builds only. Not for general use. If set, saves a
-  // Dropbox trace into an incident. This field is read by perfetto_cmd, rather
-  // than the tracing service. All fields are mandatory.
-  message IncidentReportConfig {
-    optional string destination_package = 1;
-    optional string destination_class = 2;
-    // Level of filtering in the requested incident. See |Destination| in
-    // frameworks/base/core/proto/android/privacy.proto.
-    optional int32 privacy_level = 3;
-    // If true, do not write the trace into dropbox (i.e. incident only).
-    // Otherwise, write to both dropbox and incident.
-    optional bool skip_dropbox = 4;
-  }
-  optional IncidentReportConfig incident_report_config = 25;
-
-  // DEPRECATED. Was trace_uuid, use trace_uuid_msb and trace_uuid_lsb instead.
-  reserved 26;
-  // An identifier clients can use to tie this trace to other logging.
-  // Alternative encoding of trace_uuid as two int64s.
-  optional int64 trace_uuid_msb = 27;
-  optional int64 trace_uuid_lsb = 28;
+  // Flag set by the service if, for the current packet sequence (see
+  // |trusted_packet_sequence_id|), either:
+  // * this is the first packet, or
+  // * one or multiple packets were dropped since the last packet that the
+  //   consumer read from the sequence. This can happen if chunks in the trace
+  //   buffer are overridden before the consumer could read them when the trace
+  //   is configured in ring buffer mode.
+  //
+  // When packet loss occurs, incrementally emitted data (including interned
+  // data) on the sequence should be considered invalid up until the next packet
+  // with SEQ_INCREMENTAL_STATE_CLEARED set.
+  optional bool previous_packet_dropped = 42;
 }
 
-// End of protos/perfetto/config/trace_config.proto
+// End of protos/perfetto/trace/trace_packet.proto
 
-// Begin of protos/perfetto/config/profiling/heapprofd_config.proto
+// Begin of protos/perfetto/trace/trace.proto
 
-// Configuration for go/heapprofd.
-message HeapprofdConfig {
-  message ContinuousDumpConfig {
-    // ms to wait before first dump.
-    optional uint32 dump_phase_ms = 5;
-    // ms to wait between following dumps.
-    optional uint32 dump_interval_ms = 6;
-  }
+message Trace {
+  repeated TracePacket packet = 1;
 
-  // Set to 1 for perfect accuracy.
-  // Otherwise, sample every sample_interval_bytes on average.
-  //
-  // See https://docs.perfetto.dev/#/heapprofd?id=sampling-interval for more
-  // details.
-  optional uint64 sampling_interval_bytes = 1;
-
-  // E.g. surfaceflinger, com.android.phone
-  // This input is normalized in the following way: if it contains slashes,
-  // everything up to the last slash is discarded. If it contains "@",
-  // everything after the first @ is discared.
-  // E.g. /system/bin/surfaceflinger@1.0 normalizes to surfaceflinger.
-  // This transformation is also applied to the processes' command lines when
-  // matching.
-  repeated string process_cmdline = 2;
-
-  // For watermark based triggering or local debugging.
-  repeated uint64 pid = 4;
-
-  // Profile all processes eligible for profiling on the system.
-  // See https://docs.perfetto.dev/#/heapprofd?id=target-processes for which
-  // processes are eligible.
-  //
-  // On unmodified userdebug builds, this will lead to system crashes. Zygote
-  // will crash when trying to launch a new process as it will have an
-  // unexpected open socket to heapprofd.
-  //
-  // heapprofd will likely be overloaded by the amount of data for low
-  // sampling intervals.
-  optional bool all = 5;
-
-  // Do not emit function names for mappings starting with this prefix.
-  // E.g. /system to not emit symbols for any system libraries.
-  repeated string skip_symbol_prefix = 7;
-
-  // Dump at a predefined interval.
-  optional ContinuousDumpConfig continuous_dump_config = 6;
-
-  // Size of the shared memory buffer between the profiled processes and
-  // heapprofd. Defaults to 8 MiB. If larger than 500 MiB, truncated to 500
-  // MiB.
-  //
-  // Needs to be:
-  // * at least 8192,
-  // * a power of two,
-  // * a multiple of 4096.
-  optional uint64 shmem_size_bytes = 8;
-
-  // When the shmem buffer is full, block the client instead of ending the
-  // trace. Use with caution as this will significantly slow down the target
-  // process.
-  optional bool block_client = 9;
-
-  // If set, stop the trace session after blocking the client for this
-  // timeout. Needs to be larger than 100 us, otherwise no retries are done.
-  optional uint32 block_client_timeout_us = 14;
-
-  // Do not profile processes from startup, only match already running
-  // processes.
-  //
-  // Can not be set at the same time as no_running.
-  optional bool no_startup = 10;
-
-  // Do not profile running processes. Only match processes on startup.
-  //
-  // Can not be set at the same time as no_startup.
-  optional bool no_running = 11;
-
-  // Gather information on how many bytes of allocations are on non-referenced
-  // pages. The way to use this generally is:
-  // 1. Start profile of app.
-  // 2. Start app.
-  // 3. Trigger a dump by sending SIGUSR1 to heapprofd.
-  // 4. Do operations.
-  // 5. End profile.
-  //
-  // You can then find the allocations that were not used for the operations you
-  // did in step 4.
-  optional bool idle_allocations = 12;
-
-  // Cause heapprofd to emit a single dump at the end, showing the memory usage
-  // at the point in time when the sampled heap usage of the process was at its
-  // maximum. This causes ProfilePacket.HeapSample.self_max to be set, and
-  // self_allocated and self_freed to not be set.
-  optional bool dump_at_max = 13;
+  // Do NOT add any other field here. This is just a convenience wrapper for
+  // the use case of a trace being saved to a file. There are other cases
+  // (streaming) where TracePacket are directly streamed without being wrapped
+  // in a Trace proto. Nothing should ever rely on the full trace, all the
+  // logic should be based on TracePacket(s).
 }
 
-// End of protos/perfetto/config/profiling/heapprofd_config.proto
-
-// Begin of protos/perfetto/config/profiling/java_hprof_config.proto
-
-// Configuration for go/heapprofd.
-message JavaHprofConfig {
-  // If dump_interval_ms != 0, the following configuration is used.
-  message ContinuousDumpConfig {
-    // ms to wait before first continuous dump.
-    // A dump is always created at the beginning of the trace.
-    optional uint32 dump_phase_ms = 1;
-    // ms to wait between following dumps.
-    optional uint32 dump_interval_ms = 2;
-  }
-
-  // This input is normalized in the following way: if it contains slashes,
-  // everything up to the last slash is discarded. If it contains "@",
-  // everything after the first @ is discared.
-  // E.g. /system/bin/surfaceflinger@1.0 normalizes to surfaceflinger.
-  // This transformation is also applied to the processes' command lines when
-  // matching.
-  repeated string process_cmdline = 1;
-
-  // For watermark based triggering or local debugging.
-  repeated uint64 pid = 2;
-
-  // Dump at a predefined interval.
-  optional ContinuousDumpConfig continuous_dump_config = 3;
-}
-
-// End of protos/perfetto/config/profiling/java_hprof_config.proto
-
-// Begin of protos/perfetto/config/profiling/perf_event_config.proto
-
-// TODO(b/144281346): unstable, do not use. Will change in incompatible ways.
-message PerfEventConfig {
-  optional int32 tid = 1;
-}
-
-// End of protos/perfetto/config/profiling/perf_event_config.proto
-
-// Begin of protos/perfetto/config/gpu/gpu_counter_config.proto
-
-message GpuCounterConfig {
-  // Desired sampling interval for counters.
-  optional uint64 counter_period_ns = 1;
-
-  // List of counters to be sampled. Counter IDs correspond to the ones
-  // described in GpuCounterSpec in the data source descriptor.
-  repeated uint32 counter_ids = 2;
-
-  // Sample counters by instrumenting command buffers.
-  optional bool instrumented_sampling = 3;
-
-  // Fix gpu clock rate during trace session.
-  optional bool fix_gpu_clock = 4;
-}
-
-// End of protos/perfetto/config/gpu/gpu_counter_config.proto
-
-// Begin of protos/perfetto/config/gpu/vulkan_memory_config.proto
-
-message VulkanMemoryConfig {
-  // Tracking driver memory usage events
-  optional bool track_driver_memory_usage = 1;
-
-  // Tracking device memory usage events
-  optional bool track_device_memory_usage = 2;
-}
-
-// End of protos/perfetto/config/gpu/vulkan_memory_config.proto
-
-// Begin of protos/perfetto/config/android/packages_list_config.proto
-
-// Data source that lists details (such as version code) about packages on an
-// Android device.
-message PackagesListConfig {
-  // If not empty, emit info about only the following list of package names
-  // (exact match, no regex). Otherwise, emit info about all packages.
-  repeated string package_name_filter = 1;
-}
-
-// End of protos/perfetto/config/android/packages_list_config.proto
+// End of protos/perfetto/trace/trace.proto
diff --git a/protos/perfetto/trace/profiling/BUILD.gn b/protos/perfetto/trace/profiling/BUILD.gn
index 3b607a2..3bf8905 100644
--- a/protos/perfetto/trace/profiling/BUILD.gn
+++ b/protos/perfetto/trace/profiling/BUILD.gn
@@ -19,5 +19,6 @@
     "heap_graph.proto",
     "profile_common.proto",
     "profile_packet.proto",
+    "smaps.proto",
   ]
 }
diff --git a/protos/perfetto/trace/profiling/heap_graph.proto b/protos/perfetto/trace/profiling/heap_graph.proto
index 134168c..eeb5bec 100644
--- a/protos/perfetto/trace/profiling/heap_graph.proto
+++ b/protos/perfetto/trace/profiling/heap_graph.proto
@@ -24,7 +24,13 @@
 package perfetto.protos;
 
 message ObfuscatedMember {
+  // This is the obfuscated field name relative to the class containing the
+  // ObfuscatedMember.
   optional string obfuscated_name = 1;
+  // If this is fully qualified (i.e. contains a '.') this is the deobfuscated
+  // field name including its class. Otherwise, this is this the unqualified
+  // deobfuscated field name relative to the class containing this
+  // ObfuscatedMember.
   optional string deobfuscated_name = 2;
 }
 
@@ -64,6 +70,14 @@
   optional Type root_type = 2;
 }
 
+message HeapGraphType {
+  // TODO(fmayer): Consider removing this and using the index in the repeaed
+  // field to save space.
+  optional uint64 id = 1;
+  optional uint64 location_id = 2;
+  optional string class_name = 3;
+}
+
 message HeapGraphObject {
   optional uint64 id = 1;
 
@@ -97,12 +111,22 @@
   // garbage.
   repeated HeapGraphRoot roots = 7;
 
-  // Type names used in managed heap graph.
+  // Types used in HeapGraphObjects.
+  repeated HeapGraphType types = 9;
+
+  // Legacy way to encode types. Names here are emitted by old perfetto_hprof,
+  // which do not include the class_name. Handle like a HeapGraphType with
+  // no location_id.
+  // TODO(b/153552977): Remove this. This was was not used in any publicly
+  // available release.
   repeated InternedString type_names = 3;
 
   // Field names for references in managed heap graph.
   repeated InternedString field_names = 4;
 
+  // Paths of files used in managed heap graph.
+  repeated InternedString location_names = 8;
+
   optional bool continued = 5;
   optional uint64 index = 6;
 }
diff --git a/protos/perfetto/trace/profiling/profile_packet.proto b/protos/perfetto/trace/profiling/profile_packet.proto
index 845f137..eb994b7 100644
--- a/protos/perfetto/trace/profiling/profile_packet.proto
+++ b/protos/perfetto/trace/profiling/profile_packet.proto
@@ -107,6 +107,10 @@
     // MEMORY CORRUPTION.
     optional bool buffer_corrupted = 8;
 
+    // If disconnected, this disconnect was caused by heapprofd exceeding
+    // guardrails during this profiling session.
+    optional bool hit_guardrail = 10;
+
     // Timestamp of the state of the target process that this dump represents.
     // This can be different to the timestamp of the TracePackets for various
     // reasons:
@@ -141,5 +145,85 @@
 // points in time, rather than aggregated over an interval.
 message StreamingProfilePacket {
   repeated uint64 callstack_iid = 1;  // Index into InternedData.callstacks
+  // TODO(eseckler): ThreadDescriptor-based timestamps are deprecated. Replace
+  // this with ClockSnapshot-based delta encoding instead.
   repeated int64 timestamp_delta_us = 2;
+  optional int32 process_priority = 3;
+}
+
+// Namespace for the contained enums.
+message Profiling {
+  enum CpuMode {
+    MODE_UNKNOWN = 0;
+    MODE_KERNEL = 1;
+    MODE_USER = 2;
+    // The following values aren't expected, but included for completeness:
+    MODE_HYPERVISOR = 3;
+    MODE_GUEST_KERNEL = 4;
+    MODE_GUEST_USER = 5;
+  }
+
+  // Enumeration of libunwindstack's error codes.
+  // NB: the integral representations of the two enums are different.
+  enum StackUnwindError {
+    UNWIND_ERROR_UNKNOWN = 0;
+    UNWIND_ERROR_NONE = 1;
+    UNWIND_ERROR_MEMORY_INVALID = 2;
+    UNWIND_ERROR_UNWIND_INFO = 3;
+    UNWIND_ERROR_UNSUPPORTED = 4;
+    UNWIND_ERROR_INVALID_MAP = 5;
+    UNWIND_ERROR_MAX_FRAMES_EXCEEDED = 6;
+    UNWIND_ERROR_REPEATED_FRAME = 7;
+    UNWIND_ERROR_INVALID_ELF = 8;
+  }
+}
+
+// Individual performance sampling packet payload. Typically corresponds to a
+// stack sample on a configration-dependent counter overflow.
+// Timestamps are within the root packet (in the CLOCK_BOOTTIME domain).
+// There are three distinct views of this message:
+// * completely processed sample (callstack_iid set)
+// * indication of kernel buffer data loss (kernel_records_lost set)
+// * indication of skipped samples (sample_skipped_reason set)
+message PerfSample {
+  optional uint32 cpu = 1;
+  optional uint32 pid = 2;
+  optional uint32 tid = 3;
+
+  // Execution state that the process was sampled at.
+  optional Profiling.CpuMode cpu_mode = 5;
+
+  // Unwound callstack. Might be partial, in which case a synthetic "error"
+  // frame is appended, and |unwind_error| is set appropriately.
+  optional uint64 callstack_iid = 4;
+
+  // If set, stack unwinding was incomplete due to an error.
+  // Unset values should be treated as UNWIND_ERROR_NONE.
+  oneof optional_unwind_error { Profiling.StackUnwindError unwind_error = 16; };
+
+  // If set, indicates that this message is not a sample, but rather an
+  // indication of data loss in the ring buffer allocated for |cpu|. Such data
+  // loss occurs when the kernel has insufficient ring buffer capacity to write
+  // a record (which gets discarded). A record in this context is an individual
+  // ring buffer entry, and counts more than just sample records.
+  //
+  // The |timestamp| of the packet corresponds to the time that the producer
+  // wrote the packet for trace-sorting purposes alone, and should not be
+  // interpreted relative to the sample timestamps. This field is sufficient to
+  // detect that *some* kernel data loss happened within the trace, but not the
+  // specific time bounds of that loss (which would require tracking precedessor
+  // & successor timestamps, which is not deemed necessary at the moment).
+  optional uint64 kernel_records_lost = 17;
+
+  // If set, indicates that the profiler encountered a sample that was relevant,
+  // but was skipped.
+  enum SampleSkipReason {
+    PROFILER_SKIP_UNKNOWN = 0;
+    PROFILER_SKIP_READ_STAGE = 1;
+    PROFILER_SKIP_UNWIND_STAGE = 2;
+    PROFILER_SKIP_UNWIND_ENQUEUE = 3;
+  }
+  oneof optional_sample_skipped_reason {
+    SampleSkipReason sample_skipped_reason = 18;
+  };
 }
diff --git a/src/trace_processor/global_args_tracker.cc b/protos/perfetto/trace/profiling/smaps.proto
similarity index 62%
copy from src/trace_processor/global_args_tracker.cc
copy to protos/perfetto/trace/profiling/smaps.proto
index 9966108..ec8ec2e 100644
--- a/src/trace_processor/global_args_tracker.cc
+++ b/protos/perfetto/trace/profiling/smaps.proto
@@ -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,13 +14,18 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/global_args_tracker.h"
+syntax = "proto2";
 
-namespace perfetto {
-namespace trace_processor {
+package perfetto.protos;
 
-GlobalArgsTracker::GlobalArgsTracker(TraceProcessorContext* context)
-    : context_(context) {}
+message SmapsEntry {
+  optional string path = 1;
+  optional uint64 size_kb = 2;
+  optional uint64 private_dirty_kb = 3;
+  optional uint64 swap_kb = 4;
+};
 
-}  // namespace trace_processor
-}  // namespace perfetto
+message SmapsPacket {
+  optional uint32 pid = 1;
+  repeated SmapsEntry entries = 2;
+};
diff --git a/protos/perfetto/trace/ps/process_stats.proto b/protos/perfetto/trace/ps/process_stats.proto
index 66b133d..3a334c3 100644
--- a/protos/perfetto/trace/ps/process_stats.proto
+++ b/protos/perfetto/trace/ps/process_stats.proto
@@ -24,6 +24,20 @@
 // and sometimes processes may be missing . This is because counters are
 // cached to reduce emission of counters which do not change.
 message ProcessStats {
+  // Per-thread periodically sampled stats.
+  // Note: not all of these stats will be present in every message. See the note
+  // for ProcessStats.
+  message Thread {
+    optional int32 tid = 1;
+
+    // Pairs of frequency (represented as an index to CpuInfo frequencies) and
+    // time at that frequency (represented as a number of ticks, see SystemInfo
+    // for the HZ (ticks / second) value to convert this to time).
+    // Read from /proc/tid/time_in_state.
+    repeated uint32 cpu_freq_indices = 2;
+    repeated uint64 cpu_freq_ticks = 3;
+  }
+
   message Process {
     optional int32 pid = 1;
 
@@ -40,6 +54,8 @@
     // the trace processor.
 
     optional int64 oom_score_adj = 10;
+
+    repeated Thread threads = 11;
   }
   repeated Process processes = 1;
 
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/system_info.proto b/protos/perfetto/trace/system_info.proto
index 4d2fbaa..90a7a57 100644
--- a/protos/perfetto/trace/system_info.proto
+++ b/protos/perfetto/trace/system_info.proto
@@ -28,4 +28,7 @@
 message SystemInfo {
   optional Utsname utsname = 1;
   optional string android_build_fingerprint = 2;
+
+  // Ticks per second - sysconf(_SC_CLK_TCK).
+  optional int64 hz = 3;
 }
diff --git a/protos/perfetto/trace/system_info/BUILD.gn b/protos/perfetto/trace/system_info/BUILD.gn
new file mode 100644
index 0000000..f8297ed
--- /dev/null
+++ b/protos/perfetto/trace/system_info/BUILD.gn
@@ -0,0 +1,19 @@
+# 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/proto_library.gni")
+
+perfetto_proto_library("@TYPE@") {
+  sources = [ "cpu_info.proto" ]
+}
diff --git a/protos/perfetto/trace/system_info/cpu_info.proto b/protos/perfetto/trace/system_info/cpu_info.proto
new file mode 100644
index 0000000..62f17e0
--- /dev/null
+++ b/protos/perfetto/trace/system_info/cpu_info.proto
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+// Information about CPUs from procfs and sysfs.
+message CpuInfo {
+  // Information about a single CPU.
+  message Cpu {
+    // Value of "Processor" field from /proc/cpuinfo for this CPU.
+    // Example: "AArch64 Processor rev 12 (aarch64)"
+    optional string processor = 1;
+
+    // Frequencies from
+    // /sys/devices/system/cpu/cpuX/cpufreq/scaling_available_frequencies
+    // where X is the index of this CPU.
+    repeated uint32 frequencies = 2;
+  }
+
+  // Describes available CPUs, one entry per CPU.
+  repeated Cpu cpus = 1;
+}
diff --git a/protos/perfetto/trace/test_event.proto b/protos/perfetto/trace/test_event.proto
index 58a4537..7a6902b 100644
--- a/protos/perfetto/trace/test_event.proto
+++ b/protos/perfetto/trace/test_event.proto
@@ -20,7 +20,7 @@
 
 // Event used by testing code.
 message TestEvent {
-  // Arbitary string used in tests.
+  // Arbitrary string used in tests.
   optional string str = 1;
 
   // The current value of the random number sequence used in tests.
diff --git a/protos/perfetto/trace/trace_packet.proto b/protos/perfetto/trace/trace_packet.proto
index bc3c9bf..993b05c 100644
--- a/protos/perfetto/trace/trace_packet.proto
+++ b/protos/perfetto/trace/trace_packet.proto
@@ -20,6 +20,7 @@
 import "protos/perfetto/config/trace_config.proto";
 import "protos/perfetto/trace/android/android_log.proto";
 import "protos/perfetto/trace/android/graphics_frame_event.proto";
+import "protos/perfetto/trace/android/initial_display_state.proto";
 import "protos/perfetto/trace/android/packages_list.proto";
 import "protos/perfetto/trace/chrome/chrome_benchmark_metadata.proto";
 import "protos/perfetto/trace/chrome/chrome_metadata.proto";
@@ -35,15 +36,18 @@
 import "protos/perfetto/trace/gpu/vulkan_api_event.proto";
 import "protos/perfetto/trace/interned_data/interned_data.proto";
 import "protos/perfetto/trace/perfetto/perfetto_metatrace.proto";
+import "protos/perfetto/trace/perfetto/tracing_service_event.proto";
 import "protos/perfetto/trace/power/battery_counters.proto";
 import "protos/perfetto/trace/power/power_rails.proto";
 import "protos/perfetto/trace/profiling/heap_graph.proto";
 import "protos/perfetto/trace/profiling/profile_common.proto";
 import "protos/perfetto/trace/profiling/profile_packet.proto";
+import "protos/perfetto/trace/profiling/smaps.proto";
 import "protos/perfetto/trace/ps/process_stats.proto";
 import "protos/perfetto/trace/ps/process_tree.proto";
 import "protos/perfetto/trace/sys_stats/sys_stats.proto";
 import "protos/perfetto/trace/system_info.proto";
+import "protos/perfetto/trace/system_info/cpu_info.proto";
 import "protos/perfetto/trace/trace_packet_defaults.proto";
 import "protos/perfetto/trace/track_event/process_descriptor.proto";
 import "protos/perfetto/trace/track_event/thread_descriptor.proto";
@@ -58,7 +62,7 @@
 // TracePacket(s).
 //
 // Next reserved id: 13 (up to 15).
-// Next id: 66.
+// Next id: 71.
 message TracePacket {
   // The timestamp of the TracePacket.
   // By default this timestamps refers to the trace clock (CLOCK_BOOTTIME on
@@ -109,6 +113,11 @@
     VulkanMemoryEvent vulkan_memory_event = 62;
     GpuLog gpu_log = 63;
     VulkanApiEvent vulkan_api_event = 65;
+    PerfSample perf_sample = 66;
+    CpuInfo cpu_info = 67;
+    SmapsPacket smaps_packet = 68;
+    TracingServiceEvent service_event = 69;
+    InitialDisplayState initial_display_state = 70;
 
     // Only used in profile packets.
     ProfiledFrameSymbols profiled_frame_symbols = 55;
@@ -166,7 +175,6 @@
   // proactively in advance of referring to them in later packets.
   optional InternedData interned_data = 12;
 
-
   enum SequenceFlags {
     SEQ_UNSPECIFIED = 0;
 
diff --git a/protos/perfetto/trace/track_event/BUILD.gn b/protos/perfetto/trace/track_event/BUILD.gn
index 2cb38d0..d1667d7 100644
--- a/protos/perfetto/trace/track_event/BUILD.gn
+++ b/protos/perfetto/trace/track_event/BUILD.gn
@@ -19,10 +19,12 @@
     "chrome_compositor_scheduler_state.proto",
     "chrome_histogram_sample.proto",
     "chrome_keyed_service.proto",
+    "chrome_latency_info.proto",
     "chrome_legacy_ipc.proto",
     "chrome_process_descriptor.proto",
     "chrome_thread_descriptor.proto",
     "chrome_user_event.proto",
+    "counter_descriptor.proto",
     "debug_annotation.proto",
     "log_message.proto",
     "process_descriptor.proto",
diff --git a/protos/perfetto/trace/track_event/chrome_latency_info.proto b/protos/perfetto/trace/track_event/chrome_latency_info.proto
new file mode 100644
index 0000000..88400e4
--- /dev/null
+++ b/protos/perfetto/trace/track_event/chrome_latency_info.proto
@@ -0,0 +1,83 @@
+/*
+ * 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;
+
+message ChromeLatencyInfo {
+  optional int64 trace_id = 1;
+
+  // NEXT ID: 11
+  // All step are optional but the enum is ordered (not by number) below in the
+  // order we expect them to appear if they are emitted in trace in a blocking
+  // fashion.
+  enum Step {
+    STEP_UNSPECIFIED = 0;
+    // Emitted on the browser main thread.
+    STEP_SEND_INPUT_EVENT_UI = 3;
+    // Happens on the renderer's compositor.
+    STEP_HANDLE_INPUT_EVENT_IMPL = 5;
+    STEP_DID_HANDLE_INPUT_AND_OVERSCROLL = 8;
+    // Occurs on the Renderer's main thread.
+    STEP_HANDLE_INPUT_EVENT_MAIN = 4;
+    STEP_MAIN_THREAD_SCROLL_UPDATE = 2;
+    STEP_HANDLE_INPUT_EVENT_MAIN_COMMIT = 1;
+    // Could be emitted on both the renderer's main OR compositor.
+    STEP_HANDLED_INPUT_EVENT_MAIN_OR_IMPL = 9;
+    // Optionally sometimes HANDLED_INPUT_EVENT_MAIN_OR_IMPL will proxy to the
+    // renderer's compositor and this will be emitted.
+    STEP_HANDLED_INPUT_EVENT_IMPL = 10;
+    // Renderer's compositor.
+    STEP_SWAP_BUFFERS = 6;
+    // Happens on the VizCompositor in the GPU process.
+    STEP_DRAW_AND_SWAP = 7;
+  };
+
+  optional Step step = 2;
+  optional int32 frame_tree_node_id = 3;
+
+  // This enum is a copy of LatencyComponentType enum in Chrome, located in
+  // ui/latency/latency_info.h, modulo added UNKNOWN value per protobuf
+  // practices.
+  enum LatencyComponentType {
+    COMPONENT_UNSPECIFIED = 0;
+    COMPONENT_INPUT_EVENT_LATENCY_BEGIN_RWH = 1;
+    COMPONENT_INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL = 2;
+    COMPONENT_INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL = 3;
+    COMPONENT_INPUT_EVENT_LATENCY_ORIGINAL = 4;
+    COMPONENT_INPUT_EVENT_LATENCY_UI = 5;
+    COMPONENT_INPUT_EVENT_LATENCY_RENDERER_MAIN = 6;
+    COMPONENT_INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_MAIN = 7;
+    COMPONENT_INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL = 8;
+    COMPONENT_INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT = 9;
+    COMPONENT_INPUT_EVENT_LATENCY_ACK_RWH = 10;
+    COMPONENT_INPUT_EVENT_LATENCY_RENDERER_SWAP = 11;
+    COMPONENT_DISPLAY_COMPOSITOR_RECEIVED_FRAME = 12;
+    COMPONENT_INPUT_EVENT_GPU_SWAP_BUFFER = 13;
+    COMPONENT_INPUT_EVENT_LATENCY_FRAME_SWAP = 14;
+  }
+
+  message ComponentInfo {
+    optional LatencyComponentType component_type = 1;
+
+    // Microsecond timestamp in CLOCK_MONOTONIC domain
+    optional uint64 time_us = 2;
+  };
+
+  repeated ComponentInfo component_info = 4;
+  optional bool is_coalesced = 5;
+}
diff --git a/protos/perfetto/trace/track_event/chrome_user_event.proto b/protos/perfetto/trace/track_event/chrome_user_event.proto
index 6f7c2e0..d071830 100644
--- a/protos/perfetto/trace/track_event/chrome_user_event.proto
+++ b/protos/perfetto/trace/track_event/chrome_user_event.proto
@@ -25,4 +25,7 @@
   // Chrome, these are usually static strings known at compile time, or
   // concatenations of multiple such static strings).
   optional string action = 1;
+
+  // MD5 hash of the action string.
+  optional uint64 action_hash = 2;
 }
diff --git a/protos/perfetto/trace/track_event/counter_descriptor.proto b/protos/perfetto/trace/track_event/counter_descriptor.proto
new file mode 100644
index 0000000..2821842
--- /dev/null
+++ b/protos/perfetto/trace/track_event/counter_descriptor.proto
@@ -0,0 +1,87 @@
+/*
+ * 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;
+
+// Defines properties of a counter track, e.g. for built-in counters (thread
+// time, instruction count, ..) or user-specified counters (e.g. memory usage of
+// a specific app component).
+//
+// Counter tracks only support TYPE_COUNTER track events, which specify new
+// values for the counter. For counters that require per-slice values, counter
+// values can instead be provided in a more efficient encoding via TrackEvent's
+// |extra_counter_track_uuids| and |extra_counter_values| fields. However,
+// slice-type events cannot be emitted onto a counter track.
+//
+// Values for counters that are only emitted on a single packet sequence can
+// optionally be delta-encoded, see |is_incremental|.
+//
+// Next id: 6.
+message CounterDescriptor {
+  // Built-in counters, usually with special meaning in the client library,
+  // trace processor, legacy JSON format, or UI. Trace processor will infer a
+  // track name from the enum value if none is provided in TrackDescriptor.
+  enum BuiltinCounterType {
+    COUNTER_UNSPECIFIED = 0;
+
+    // Thread-scoped counters. The thread's track should be specified via
+    // |parent_uuid| in the TrackDescriptor for such a counter.
+    COUNTER_THREAD_TIME_NS = 1;            // implies UNIT_TIME_NS.
+    COUNTER_THREAD_INSTRUCTION_COUNT = 2;  // implies UNIT_COUNT.
+  }
+
+  // Type of the values for the counters - to supply lower granularity units,
+  // see also |unit_multiplier|.
+  enum Unit {
+    UNIT_UNSPECIFIED = 0;
+    UNIT_TIME_NS = 1;
+    UNIT_COUNT = 2;
+    UNIT_SIZE_BYTES = 3;
+    // TODO(eseckler): Support more units as necessary.
+  }
+
+  // For built-in counters (e.g. thread time). Custom user-specified counters
+  // (e.g. those emitted by TRACE_COUNTER macros of the client library)
+  // shouldn't set this, and instead provide a counter name via TrackDescriptor.
+  optional BuiltinCounterType type = 1;
+
+  // Names of categories of the counter (usually for user-specified counters).
+  // In the client library, categories are a way to turn groups of individual
+  // counters (or events) on or off.
+  repeated string categories = 2;
+
+  // Type of the counter's values. Built-in counters imply a value for this
+  // field.
+  optional Unit unit = 3;
+
+  // Multiplication factor of this counter's values, e.g. to supply
+  // COUNTER_THREAD_TIME_NS timestamps in microseconds instead.
+  optional int64 unit_multiplier = 4;
+
+  // Whether values for this counter are provided as delta values. Only
+  // supported for counters that are emitted on a single packet-sequence (e.g.
+  // thread time). Counter values in subsequent packets on the current packet
+  // sequence will be interpreted as delta values from the sequence's most
+  // recent value for the counter. When incremental state is cleared, the
+  // counter value is considered to be reset to 0. Thus, the first value after
+  // incremental state is cleared is effectively an absolute value.
+  optional bool is_incremental = 5;
+
+  // TODO(eseckler): Support arguments describing the counter (?).
+  // repeated DebugAnnotation debug_annotations;
+}
diff --git a/protos/perfetto/trace/track_event/track_descriptor.proto b/protos/perfetto/trace/track_event/track_descriptor.proto
index 5254b28..66b50f4 100644
--- a/protos/perfetto/trace/track_event/track_descriptor.proto
+++ b/protos/perfetto/trace/track_event/track_descriptor.proto
@@ -20,6 +20,7 @@
 import "protos/perfetto/trace/track_event/chrome_thread_descriptor.proto";
 import "protos/perfetto/trace/track_event/process_descriptor.proto";
 import "protos/perfetto/trace/track_event/thread_descriptor.proto";
+import "protos/perfetto/trace/track_event/counter_descriptor.proto";
 
 package perfetto.protos;
 
@@ -36,7 +37,7 @@
 // |TrackEvent::track_uuid|. It is possible but not necessary to emit a
 // TrackDescriptor for this implicit track.
 //
-// Next id: 8.
+// Next id: 9.
 message TrackDescriptor {
   // Unique ID that identifies this track. This ID is global to the whole trace.
   // Producers should ensure that it is unlikely to clash with IDs emitted by
@@ -49,9 +50,13 @@
   // A parent track reference can be used to describe relationships between
   // tracks. For example, to define an asynchronous track which is scoped to a
   // specific process, specify the uuid for that process's process track here.
+  // Similarly, to associate a COUNTER_THREAD_TIME_NS counter track with a
+  // thread, specify the uuid for that thread's thread track here.
   optional uint64 parent_uuid = 5;
 
-  // Name of the track.
+  // Name of the track. Optional - if unspecified, it may be derived from the
+  // process/thread name (process/thread tracks), the first event's name (async
+  // tracks), or counter name (counter tracks).
   optional string name = 2;
 
   // Associate the track with a process, making it the process-global track.
@@ -74,4 +79,9 @@
   // view.
   optional ThreadDescriptor thread = 4;
   optional ChromeThreadDescriptor chrome_thread = 7;
+
+  // Descriptor for a counter track. If set, the track will only support
+  // TYPE_COUNTER TrackEvents (and values provided via TrackEvent's
+  // |extra_counter_values|).
+  optional CounterDescriptor counter = 8;
 }
diff --git a/protos/perfetto/trace/track_event/track_event.proto b/protos/perfetto/trace/track_event/track_event.proto
index c566648..3825b76 100644
--- a/protos/perfetto/trace/track_event/track_event.proto
+++ b/protos/perfetto/trace/track_event/track_event.proto
@@ -22,6 +22,7 @@
 import "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto";
 import "protos/perfetto/trace/track_event/chrome_histogram_sample.proto";
 import "protos/perfetto/trace/track_event/chrome_keyed_service.proto";
+import "protos/perfetto/trace/track_event/chrome_latency_info.proto";
 import "protos/perfetto/trace/track_event/chrome_legacy_ipc.proto";
 import "protos/perfetto/trace/track_event/chrome_user_event.proto";
 
@@ -86,7 +87,7 @@
 // their default track association) can be emitted as part of a
 // TrackEventDefaults message.
 //
-// Next reserved id: 12 (up to 15). Next id: 29.
+// Next reserved id: 13 (up to 15). Next id: 32.
 message TrackEvent {
   // Names of categories of the event. In the client library, categories are a
   // way to turn groups of individual events on or off.
@@ -133,7 +134,11 @@
     // of slice events on the same track.
     TYPE_INSTANT = 3;
 
-    // TODO(eseckler): Add support for counters.
+    // Event that provides a value for a counter track. |track_uuid| should
+    // refer to a counter track and |counter_value| set to the new value. Note
+    // that most other TrackEvent fields (e.g. categories, name, ..) are not
+    // supported for TYPE_COUNTER events. See also CounterDescriptor.
+    TYPE_COUNTER = 4;
   }
   optional Type type = 9;
 
@@ -144,32 +149,33 @@
   // implicit trace-global track (uuid 0). See TrackDescriptor::uuid.
   optional uint64 track_uuid = 11;
 
+  // A new value for a counter track. |track_uuid| should refer to a track with
+  // a CounterDescriptor, and |type| should be TYPE_COUNTER. For a more
+  // efficient encoding of counter values that are sampled at the beginning/end
+  // of a slice, see |extra_counter_values| and |extra_counter_track_uuids|.
+  // Counter values can optionally be encoded in as delta values (positive or
+  // negative) on each packet sequence (see CounterIncrementalBase).
+  optional int64 counter_value = 30;
+
+  // To encode counter values more efficiently, we support attaching additional
+  // counter values to a TrackEvent of any type. All values will share the same
+  // timestamp specified in the TracePacket. The value at
+  // extra_counter_values[N] is for the counter track referenced by
+  // extra_counter_track_uuids[N].
+  //
+  // |extra_counter_track_uuids| may also be set via TrackEventDefaults. There
+  // should always be equal or more uuids than values. It is valid to set more
+  // uuids (e.g. via defaults) than values. If uuids are specified in
+  // TrackEventDefaults and a TrackEvent, the TrackEvent uuids override the
+  // default uuid list.
+  //
+  // For example, this allows snapshotting the thread time clock at each
+  // thread-track BEGIN and END event to capture the cpu time delta of a slice.
+  repeated uint64 extra_counter_track_uuids = 31;
+  repeated int64 extra_counter_values = 12;
+
   // TODO(eseckler): Add flow event support.
 
-  // TODO(eseckler): Encode thread_time and thread_instruction_count using a
-  // ClockSnapshot + clock id instead of ThreadDescriptor's reference values.
-
-  // CPU time for the current thread (e.g., CLOCK_THREAD_CPUTIME_ID) in
-  // microseconds.
-  oneof thread_time {
-    // Delta timestamp value since the last TrackEvent or ThreadDescriptor. To
-    // calculate the absolute timestamp value, sum up all delta values of the
-    // preceding TrackEvents since the last ThreadDescriptor and add the sum to
-    // the |reference_timestamp| in ThreadDescriptor. This value should always
-    // be positive.
-    int64 thread_time_delta_us = 2;
-    // This is a one-off absolute value that does not affect delta timestamp
-    // computation in subsequent TrackEvents.
-    int64 thread_time_absolute_us = 17;
-  }
-
-  // Value of the instruction counter for the current thread.
-  oneof thread_instruction_count {
-    // Same encoding as |thread_time| field above.
-    int64 thread_instruction_count_delta = 8;
-    int64 thread_instruction_count_absolute = 20;
-  }
-
   // ---------------------------------------------------------------------------
   // TrackEvent arguments:
   // ---------------------------------------------------------------------------
@@ -186,6 +192,7 @@
   optional ChromeKeyedService chrome_keyed_service = 26;
   optional ChromeLegacyIpc chrome_legacy_ipc = 27;
   optional ChromeHistogramSample chrome_histogram_sample = 28;
+  optional ChromeLatencyInfo chrome_latency_info = 29;
   // New argument types go here :)
 
   // ---------------------------------------------------------------------------
@@ -209,6 +216,33 @@
     int64 timestamp_absolute_us = 16;
   }
 
+  // Deprecated. Use |extra_counter_values| and |extra_counter_track_uuids| to
+  // encode thread time instead.
+  //
+  // CPU time for the current thread (e.g., CLOCK_THREAD_CPUTIME_ID) in
+  // microseconds.
+  oneof thread_time {
+    // Delta timestamp value since the last TrackEvent or ThreadDescriptor. To
+    // calculate the absolute timestamp value, sum up all delta values of the
+    // preceding TrackEvents since the last ThreadDescriptor and add the sum to
+    // the |reference_timestamp| in ThreadDescriptor. This value should always
+    // be positive.
+    int64 thread_time_delta_us = 2;
+    // This is a one-off absolute value that does not affect delta timestamp
+    // computation in subsequent TrackEvents.
+    int64 thread_time_absolute_us = 17;
+  }
+
+  // Deprecated. Use |extra_counter_values| and |extra_counter_track_uuids| to
+  // encode thread instruction count instead.
+  //
+  // Value of the instruction counter for the current thread.
+  oneof thread_instruction_count {
+    // Same encoding as |thread_time| field above.
+    int64 thread_instruction_count_delta = 8;
+    int64 thread_instruction_count_absolute = 20;
+  }
+
   // Apart from {category, time, thread time, tid, pid}, other legacy trace
   // event attributes are initially simply proxied for conversion to a JSON
   // trace. We intend to gradually transition these attributes to similar native
@@ -279,6 +313,7 @@
 // corresponding fields in TrackEvent.
 message TrackEventDefaults {
   optional uint64 track_uuid = 11;
+  repeated uint64 extra_counter_track_uuids = 31;
 
   // TODO(eseckler): Support default values for more TrackEvent fields.
 }
diff --git a/protos/perfetto/trace_processor/BUILD.gn b/protos/perfetto/trace_processor/BUILD.gn
index e645a3a..2446bef 100644
--- a/protos/perfetto/trace_processor/BUILD.gn
+++ b/protos/perfetto/trace_processor/BUILD.gn
@@ -21,11 +21,10 @@
   foreach(source, trace_processor_protos) {
     sources += [ "$source.proto" ]
   }
+  deps = [ "../metrics:@TYPE@" ]
 }
 
 perfetto_proto_library("metrics_impl_zero") {
   proto_generators = [ "zero" ]
-  sources = [
-    "metrics_impl.proto",
-  ]
+  sources = [ "metrics_impl.proto" ]
 }
diff --git a/protos/perfetto/trace_processor/trace_processor.proto b/protos/perfetto/trace_processor/trace_processor.proto
index d24d202..5812bb9 100644
--- a/protos/perfetto/trace_processor/trace_processor.proto
+++ b/protos/perfetto/trace_processor/trace_processor.proto
@@ -16,7 +16,9 @@
 
 syntax = "proto2";
 
-package perfetto.trace_processor.protos;
+package perfetto.protos;
+
+import "protos/perfetto/metrics/metrics.proto";
 
 // This file defines the schema for {,un}marshalling arguments and return values
 // when interfacing to the trace processor binary interface.
@@ -82,3 +84,14 @@
   // trace_processor_shell -D trace_file.pftrace .
   optional string loaded_trace_name = 1;
 }
+
+// Input for the /compute_metric endpoint.
+message ComputeMetricArgs {
+  repeated string metric_names = 1;
+}
+
+// Output for the /compute_metric endpoint.
+message ComputeMetricResult {
+  optional perfetto.protos.TraceMetrics metrics = 1;
+  optional string error = 2;
+}
\ No newline at end of file
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/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/android_internal/atrace_hal.cc b/src/android_internal/atrace_hal.cc
index 67e445d..486a44a 100644
--- a/src/android_internal/atrace_hal.cc
+++ b/src/android_internal/atrace_hal.cc
@@ -22,10 +22,11 @@
 namespace perfetto {
 namespace android_internal {
 
-using android::hardware::atrace::V1_0::IAtraceDevice;
-using android::hardware::atrace::V1_0::TracingCategory;
+using android::hardware::hidl_string;
 using android::hardware::hidl_vec;
 using android::hardware::Return;
+using android::hardware::atrace::V1_0::IAtraceDevice;
+using android::hardware::atrace::V1_0::TracingCategory;
 
 namespace {
 
@@ -40,7 +41,7 @@
 
 }  // namespace
 
-bool GetCategories(TracingVendorCategory* categories, size_t* size_of_arr) {
+bool ListCategories(TracingVendorCategory* categories, size_t* size_of_arr) {
   const size_t in_array_size = *size_of_arr;
   *size_of_arr = 0;
   if (!GetService())
@@ -64,5 +65,30 @@
   return true;
 }
 
+bool EnableCategories(const char** categories, size_t categories_count) {
+  if (!GetService())
+    return false;
+  std::vector<hidl_string> args;
+  args.resize(categories_count);
+  for (size_t i = 0; i < categories_count; ++i) {
+    args[i] = categories[i];
+  }
+  g_atraceHal->enableCategories(args);
+  // TODO(hjd): Check status.
+  return true;
+}
+
+bool DisableAllCategories() {
+  if (!GetService())
+    return false;
+  g_atraceHal->disableAllCategories();
+  // TODO(hjd): Check status.
+  return true;
+}
+
+void ForgetService() {
+  g_atraceHal = nullptr;
+}
+
 }  // namespace android_internal
 }  // namespace perfetto
diff --git a/src/android_internal/atrace_hal.h b/src/android_internal/atrace_hal.h
index 18d099d..782f800 100644
--- a/src/android_internal/atrace_hal.h
+++ b/src/android_internal/atrace_hal.h
@@ -42,7 +42,14 @@
 // These functions are not thread safe unless specified otherwise.
 
 bool __attribute__((visibility("default")))
-GetCategories(TracingVendorCategory*, size_t* size_of_arr);
+ListCategories(TracingVendorCategory*, size_t* size_of_arr);
+
+bool __attribute__((visibility("default")))
+EnableCategories(const char** categories, size_t categories_count);
+
+bool __attribute__((visibility("default"))) DisableAllCategories();
+
+void __attribute__((visibility("default"))) ForgetService();
 
 }  // extern "C"
 
diff --git a/src/base/BUILD.gn b/src/base/BUILD.gn
index c786391..1749b7b 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",
@@ -35,6 +33,7 @@
     "string_splitter.cc",
     "string_utils.cc",
     "string_view.cc",
+    "subprocess.cc",
     "thread_checker.cc",
     "time.cc",
     "uuid.cc",
@@ -61,9 +60,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 +81,7 @@
       "../../include/perfetto/ext/base",
       "../../include/perfetto/ext/base",
     ]
-    sources = [
-      "unix_socket.cc",
-    ]
+    sources = [ "unix_socket.cc" ]
   }
 }
 
@@ -132,6 +127,7 @@
     "string_utils_unittest.cc",
     "string_view_unittest.cc",
     "string_writer_unittest.cc",
+    "subprocess_unittest.cc",
     "time_unittest.cc",
     "uuid_unittest.cc",
     "weak_ptr_unittest.cc",
@@ -165,8 +161,6 @@
       "../../gn:benchmark",
       "../../gn:default_deps",
     ]
-    sources = [
-      "flat_set_benchmark.cc",
-    ]
+    sources = [ "flat_set_benchmark.cc" ]
   }
 }
diff --git a/src/base/string_utils_unittest.cc b/src/base/string_utils_unittest.cc
index d67976f..775e5ca 100644
--- a/src/base/string_utils_unittest.cc
+++ b/src/base/string_utils_unittest.cc
@@ -77,20 +77,56 @@
   EXPECT_EQ(StringToUInt32("0"), make_optional<uint32_t>(0U));
   EXPECT_EQ(StringToUInt32("1"), make_optional<uint32_t>(1U));
   EXPECT_EQ(StringToUInt32("42"), make_optional<uint32_t>(42U));
+  EXPECT_EQ(StringToUInt32("a", 16), make_optional<uint32_t>(10U));
+  EXPECT_EQ(StringToUInt32("fffffff0", 16),
+            make_optional<uint32_t>(0xfffffff0));
   EXPECT_EQ(StringToUInt32(""), nullopt);
   EXPECT_EQ(StringToUInt32("!?"), nullopt);
   EXPECT_EQ(StringToUInt32("abc"), nullopt);
   EXPECT_EQ(StringToUInt32("123 abc"), nullopt);
+  EXPECT_EQ(StringToUInt32("beefz", 16), nullopt);
 }
 
 TEST(StringUtilsTest, StringToInt32) {
   EXPECT_EQ(StringToInt32("0"), make_optional<int32_t>(0));
   EXPECT_EQ(StringToInt32("1"), make_optional<int32_t>(1));
   EXPECT_EQ(StringToInt32("-42"), make_optional<int32_t>(-42));
+  EXPECT_EQ(StringToInt32("42", 16), make_optional<int32_t>(0x42));
+  EXPECT_EQ(StringToInt32("7ffffffe", 16), make_optional<int32_t>(0x7ffffffe));
   EXPECT_EQ(StringToInt32(""), nullopt);
   EXPECT_EQ(StringToInt32("!?"), nullopt);
   EXPECT_EQ(StringToInt32("abc"), nullopt);
   EXPECT_EQ(StringToInt32("123 abc"), nullopt);
+  EXPECT_EQ(StringToInt32("beefz", 16), nullopt);
+}
+
+TEST(StringUtilsTest, StringToUInt64) {
+  EXPECT_EQ(StringToUInt64("0"), make_optional<uint64_t>(0u));
+  EXPECT_EQ(StringToUInt64("1"), make_optional<uint64_t>(1u));
+  EXPECT_EQ(StringToUInt64("5000000000"),
+            make_optional<uint64_t>(5000000000ULL));
+  EXPECT_EQ(StringToUInt64("7ffffffffffffffe", 16),
+            make_optional<uint64_t>(0x7ffffffffffffffeULL));
+  EXPECT_EQ(StringToUInt64("9ffffffffffffffe", 16),
+            make_optional<uint64_t>(0x9ffffffffffffffeULL));
+  EXPECT_EQ(StringToUInt64(""), nullopt);
+  EXPECT_EQ(StringToUInt64("abc"), nullopt);
+  EXPECT_EQ(StringToUInt64("beefz", 16), nullopt);
+}
+
+TEST(StringUtilsTest, StringToInt64) {
+  EXPECT_EQ(StringToInt64("0"), make_optional<int64_t>(0));
+  EXPECT_EQ(StringToInt64("1"), make_optional<int64_t>(1));
+  EXPECT_EQ(StringToInt64("-5000000000"),
+            make_optional<int64_t>(-5000000000LL));
+  EXPECT_EQ(StringToInt64("5000000000"), make_optional<int64_t>(5000000000LL));
+  EXPECT_EQ(StringToInt64("7ffffffffffffffe", 16),
+            make_optional<int64_t>(0x7ffffffffffffffeLL));
+  EXPECT_EQ(StringToInt64("9ffffffe", 16),
+            make_optional<int64_t>(0x9ffffffeLL));
+  EXPECT_EQ(StringToInt64(""), nullopt);
+  EXPECT_EQ(StringToInt64("abc"), nullopt);
+  EXPECT_EQ(StringToInt64("beefz", 16), nullopt);
 }
 
 TEST(StringUtilsTest, StringToDouble) {
diff --git a/src/base/subprocess.cc b/src/base/subprocess.cc
new file mode 100644
index 0000000..6be24bd
--- /dev/null
+++ b/src/base/subprocess.cc
@@ -0,0 +1,436 @@
+/*
+ * 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 "perfetto/ext/base/subprocess.h"
+
+#if PERFETTO_HAS_SUBPROCESS()
+
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <thread>
+
+#include "perfetto/base/build_config.h"
+#include "perfetto/base/logging.h"
+#include "perfetto/base/time.h"
+#include "perfetto/ext/base/utils.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <sys/prctl.h>
+#endif
+
+// In MacOS this is not defined in any header.
+extern "C" char** environ;
+
+namespace perfetto {
+namespace base {
+
+namespace {
+
+struct ChildProcessArgs {
+  Subprocess::Args* create_args;
+  const char* exec_cmd = nullptr;
+  std::vector<char*> argv;
+  std::vector<char*> env;
+  int stdin_pipe_rd = -1;
+  int stdouterr_pipe_wr = -1;
+};
+
+// Don't add any dynamic allocation in this function. This will be invoked
+// under a fork(), potentially in a state where the allocator lock is held.
+void __attribute__((noreturn)) ChildProcess(ChildProcessArgs* args) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  // In no case we want a child process to outlive its parent process. This is
+  // relevant for tests, so that a test failure/crash doesn't leave child
+  // processes around that get reparented to init.
+  prctl(PR_SET_PDEATHSIG, SIGKILL);
+#endif
+
+  auto die = [args](const char* err) __attribute__((noreturn)) {
+    base::ignore_result(write(args->stdouterr_pipe_wr, err, strlen(err)));
+    base::ignore_result(write(args->stdouterr_pipe_wr, "\n", 1));
+    // From https://www.gnu.org/software/libc/manual/html_node/Exit-Status.html
+    // "In particular, the value 128 is used to indicate failure to execute
+    // another program in a subprocess. This convention is not universally
+    // obeyed, but it is a good idea to follow it in your programs."
+    _exit(128);
+  };
+
+  auto set_fd_close_on_exec = [&die](int fd, bool close_on_exec) {
+    int flags = fcntl(fd, F_GETFD, 0);
+    if (flags < 0)
+      die("fcntl(F_GETFD) failed");
+    flags = close_on_exec ? (flags | FD_CLOEXEC) : (flags & ~FD_CLOEXEC);
+    if (fcntl(fd, F_SETFD, flags) < 0)
+      die("fcntl(F_SETFD) failed");
+  };
+
+  if (getppid() == 1)
+    die("terminating because parent process died");
+
+  if (dup2(args->stdin_pipe_rd, STDIN_FILENO) == -1)
+    die("Failed to dup2(STDIN)");
+  close(args->stdin_pipe_rd);
+
+  switch (args->create_args->stdout_mode) {
+    case Subprocess::kInherit:
+      break;
+    case Subprocess::kDevNull: {
+      if (dup2(open("/dev/null", O_RDWR), STDOUT_FILENO) == -1)
+        die("Failed to dup2(STDOUT)");
+      break;
+    }
+    case Subprocess::kBuffer:
+      if (dup2(args->stdouterr_pipe_wr, STDOUT_FILENO) == -1)
+        die("Failed to dup2(STDOUT)");
+      break;
+  }
+
+  switch (args->create_args->stderr_mode) {
+    case Subprocess::kInherit:
+      break;
+    case Subprocess::kDevNull: {
+      if (dup2(open("/dev/null", O_RDWR), STDERR_FILENO) == -1)
+        die("Failed to dup2(STDERR)");
+      break;
+    }
+    case Subprocess::kBuffer:
+      if (dup2(args->stdouterr_pipe_wr, STDERR_FILENO) == -1)
+        die("Failed to dup2(STDERR)");
+      break;
+  }
+
+  // Close all FDs % stdin/out/err and the ones that the client explicitly
+  // asked to retain. The reason for this is twofold:
+  // 1. For exec-only (i.e. entrypoint == empty) cases: it avoids leaking FDs
+  //    that didn't get marked as O_CLOEXEC by accident.
+  // 2. In fork() mode (entrypoint not empty) avoids retaining a dup of eventfds
+  //    that would prevent the parent process to receive EOFs (tests usually use
+  //    pipes as a synchronization mechanism between subprocesses).
+  const auto& preserve_fds = args->create_args->preserve_fds;
+  for (int i = 0; i < 512; i++) {
+    if (i != STDIN_FILENO && i != STDERR_FILENO && i != STDOUT_FILENO &&
+        i != args->stdouterr_pipe_wr &&
+        !std::count(preserve_fds.begin(), preserve_fds.end(), i)) {
+      close(i);
+    }
+  }
+
+  // Clears O_CLOEXEC from stdin/out/err. These are the only FDs that we want
+  // to be preserved after the exec().
+  set_fd_close_on_exec(STDIN_FILENO, false);
+  set_fd_close_on_exec(STDOUT_FILENO, false);
+  set_fd_close_on_exec(STDERR_FILENO, false);
+
+  // If the caller specified a std::function entrypoint, run that first.
+  if (args->create_args->entrypoint_for_testing)
+    args->create_args->entrypoint_for_testing();
+
+  // If the caller specified only an entrypoint, without any args, exit now.
+  // Otherwise proceed with the exec() below.
+  if (!args->exec_cmd)
+    _exit(0);
+
+  // If |args[0]| is a path use execv() (which takes a path), othewise use
+  // exevp(), which uses the shell and follows PATH.
+  if (strchr(args->exec_cmd, '/')) {
+    char** env = args->env.empty() ? environ : args->env.data();
+    execve(args->exec_cmd, args->argv.data(), env);
+  } else {
+    // There is no execvpe() on Mac.
+    if (!args->env.empty())
+      die("A full path is required for |exec_cmd| when setting |env|");
+    execvp(args->exec_cmd, args->argv.data());
+  }
+
+  // Reached only if execv fails.
+  die("execve() failed");
+}
+
+}  // namespace
+
+Subprocess::Args::Args(Args&&) noexcept = default;
+Subprocess::Args& Subprocess::Args::operator=(Args&&) = default;
+
+Subprocess::Subprocess(std::initializer_list<std::string> _args)
+    : args(_args) {}
+
+Subprocess::Subprocess(Subprocess&&) noexcept = default;
+Subprocess& Subprocess::operator=(Subprocess&&) = default;
+
+Subprocess::~Subprocess() {
+  if (status_ == kRunning)
+    KillAndWaitForTermination();
+  PERFETTO_CHECK(!waitpid_thread_.joinable());
+}
+
+void Subprocess::Start() {
+  ChildProcessArgs proc_args;
+  proc_args.create_args = &args;
+
+  // Setup argv.
+  if (!args.exec_cmd.empty()) {
+    proc_args.exec_cmd = args.exec_cmd[0].c_str();
+    for (const std::string& arg : args.exec_cmd)
+      proc_args.argv.push_back(const_cast<char*>(arg.c_str()));
+    proc_args.argv.push_back(nullptr);
+
+    if (!args.argv0_override.empty())
+      proc_args.argv[0] = const_cast<char*>(args.argv0_override.c_str());
+  }
+
+  // Setup env.
+  if (!args.env.empty()) {
+    for (const std::string& str : args.env)
+      proc_args.env.push_back(const_cast<char*>(str.c_str()));
+    proc_args.env.push_back(nullptr);
+  }
+
+  // Setup the pipes for stdin/err redirection.
+  stdin_pipe_ = base::Pipe::Create(base::Pipe::kWrNonBlock);
+  proc_args.stdin_pipe_rd = *stdin_pipe_.rd;
+  stdouterr_pipe_ = base::Pipe::Create(base::Pipe::kRdNonBlock);
+  proc_args.stdouterr_pipe_wr = *stdouterr_pipe_.wr;
+
+  // Spawn the child process that will exec().
+  pid_ = fork();
+  PERFETTO_CHECK(pid_ >= 0);
+  if (pid_ == 0) {
+    // Close the parent-ends of the pipes.
+    stdin_pipe_.wr.reset();
+    stdouterr_pipe_.rd.reset();
+    ChildProcess(&proc_args);
+    // ChildProcess() doesn't return, not even in case of failures.
+    PERFETTO_FATAL("not reached");
+  }
+
+  status_ = kRunning;
+
+  // Close the child-end of the pipes.
+  // Deliberately NOT closing the stdin_pipe_.rd. This is to avoid crashing
+  // with a SIGPIPE if the process exits without consuming its stdin, while
+  // the parent tries to write() on the other end of the stdin pipe.
+  stdouterr_pipe_.wr.reset();
+
+  // Spawn a thread that is blocked on waitpid() and writes the termination
+  // status onto a pipe. The problem here is that waipid() doesn't have a
+  // timeout option and can't be passed to poll(). The alternative would be
+  // using a SIGCHLD handler, but anecdotally signal handlers introduce more
+  // problems than what they solve.
+  exit_status_pipe_ = base::Pipe::Create(base::Pipe::kRdNonBlock);
+
+  // Both ends of the pipe are closed after the thread.join().
+  int pid = pid_;
+  int exit_status_pipe_wr = exit_status_pipe_.wr.release();
+  waitpid_thread_ = std::thread([pid, exit_status_pipe_wr] {
+    int pid_stat = -1;
+    int wait_res = PERFETTO_EINTR(waitpid(pid, &pid_stat, 0));
+    PERFETTO_CHECK(wait_res == pid);
+    base::ignore_result(PERFETTO_EINTR(
+        write(exit_status_pipe_wr, &pid_stat, sizeof(pid_stat))));
+    PERFETTO_CHECK(close(exit_status_pipe_wr) == 0 || errno == EINTR);
+  });
+}
+
+Subprocess::Status Subprocess::Poll() {
+  if (status_ != kRunning)
+    return status_;  // Nothing to poll.
+  while (PollInternal(0 /* don't block*/)) {
+  }
+  return status_;
+}
+
+// |timeout_ms| semantic:
+//   -1: Block indefinitely.
+//    0: Don't block, return immediately.
+//   >0: Block for at most X ms.
+// Returns:
+//  True: Read at least one fd (so there might be more queued).
+//  False: if all fds reached quiescent (no data to read/write).
+bool Subprocess::PollInternal(int poll_timeout_ms) {
+  struct pollfd fds[3]{};
+  size_t num_fds = 0;
+  if (exit_status_pipe_.rd) {
+    fds[num_fds].fd = *exit_status_pipe_.rd;
+    fds[num_fds].events = POLLIN;
+    num_fds++;
+  }
+  if (stdouterr_pipe_.rd) {
+    fds[num_fds].fd = *stdouterr_pipe_.rd;
+    fds[num_fds].events = POLLIN;
+    num_fds++;
+  }
+  if (stdin_pipe_.wr) {
+    fds[num_fds].fd = *stdin_pipe_.wr;
+    fds[num_fds].events = POLLOUT;
+    num_fds++;
+  }
+
+  if (num_fds == 0)
+    return false;
+
+  auto nfds = static_cast<nfds_t>(num_fds);
+  int poll_res = PERFETTO_EINTR(poll(fds, nfds, poll_timeout_ms));
+  PERFETTO_CHECK(poll_res >= 0);
+
+  TryReadStdoutAndErr();
+  TryPushStdin();
+  TryReadExitStatus();
+
+  return poll_res > 0;
+}
+
+bool Subprocess::Wait(int timeout_ms) {
+  PERFETTO_CHECK(status_ != kNotStarted);
+
+  // Break out of the loop only after both conditions are satisfied:
+  // - All stdout/stderr data has been read (if kBuffer).
+  // - The process exited.
+  // Note that the two events can happen arbitrary order. After the process
+  // exits, there might be still data in the pipe buffer, which we want to
+  // read fully.
+  //
+  // Instead, don't wait on the stdin to be fully written. The child process
+  // might exit prematurely (or crash). If that happens, we can end up in a
+  // state where the write(stdin_pipe_.wr) will never unblock.
+
+  const int64_t t_start = base::GetWallTimeMs().count();
+  while (exit_status_pipe_.rd || stdouterr_pipe_.rd) {
+    int poll_timeout_ms = -1;  // Block until a FD is ready.
+    if (timeout_ms > 0) {
+      const int64_t now = GetWallTimeMs().count();
+      poll_timeout_ms = timeout_ms - static_cast<int>(now - t_start);
+      if (poll_timeout_ms <= 0)
+        return false;
+    }
+    PollInternal(poll_timeout_ms);
+  }  // while(...)
+  return true;
+}
+
+bool Subprocess::Call(int timeout_ms) {
+  PERFETTO_CHECK(status_ == kNotStarted);
+  Start();
+
+  if (!Wait(timeout_ms)) {
+    KillAndWaitForTermination();
+    // TryReadExitStatus must have joined the thread.
+    PERFETTO_DCHECK(!waitpid_thread_.joinable());
+  }
+  PERFETTO_DCHECK(status_ != kRunning);
+  return status_ == kExited && returncode_ == 0;
+}
+
+void Subprocess::TryReadExitStatus() {
+  if (!exit_status_pipe_.rd)
+    return;
+
+  int pid_stat = -1;
+  int64_t rsize =
+      PERFETTO_EINTR(read(*exit_status_pipe_.rd, &pid_stat, sizeof(pid_stat)));
+  if (rsize < 0 && errno == EAGAIN)
+    return;
+
+  if (rsize > 0) {
+    PERFETTO_CHECK(rsize == sizeof(pid_stat));
+  } else if (rsize < 0) {
+    PERFETTO_PLOG("Subprocess read(exit_status_pipe_) failed");
+  }
+  waitpid_thread_.join();
+  exit_status_pipe_.rd.reset();
+
+  if (WIFEXITED(pid_stat)) {
+    returncode_ = WEXITSTATUS(pid_stat);
+    status_ = kExited;
+  } else if (WIFSIGNALED(pid_stat)) {
+    returncode_ = 128 + WTERMSIG(pid_stat);  // Follow bash convention.
+    status_ = kKilledBySignal;
+  } else {
+    PERFETTO_FATAL("waitpid() returned an unexpected value (0x%x)", pid_stat);
+  }
+}
+
+// If the stidn pipe is still open, push input data and close it at the end.
+void Subprocess::TryPushStdin() {
+  if (!stdin_pipe_.wr)
+    return;
+
+  PERFETTO_DCHECK(args.input.empty() || input_written_ < args.input.size());
+  if (args.input.size()) {
+    int64_t wsize =
+        PERFETTO_EINTR(write(*stdin_pipe_.wr, &args.input[input_written_],
+                             args.input.size() - input_written_));
+    if (wsize < 0 && errno == EAGAIN)
+      return;
+
+    if (wsize >= 0) {
+      // Whether write() can return 0 is one of the greatest mysteries of UNIX.
+      // Just ignore it.
+      input_written_ += static_cast<size_t>(wsize);
+    } else {
+      PERFETTO_PLOG("Subprocess write(stdin) failed");
+      stdin_pipe_.wr.reset();
+    }
+  }
+  PERFETTO_DCHECK(input_written_ <= args.input.size());
+  if (input_written_ == args.input.size())
+    stdin_pipe_.wr.reset();  // Close stdin.
+}
+
+void Subprocess::TryReadStdoutAndErr() {
+  if (!stdouterr_pipe_.rd)
+    return;
+  char buf[4096];
+  int64_t rsize = PERFETTO_EINTR(read(*stdouterr_pipe_.rd, buf, sizeof(buf)));
+  if (rsize < 0 && errno == EAGAIN)
+    return;
+
+  if (rsize > 0) {
+    output_.append(buf, static_cast<size_t>(rsize));
+  } else if (rsize == 0 /* EOF */) {
+    stdouterr_pipe_.rd.reset();
+  } else {
+    PERFETTO_PLOG("Subprocess read(stdout/err) failed");
+    stdouterr_pipe_.rd.reset();
+  }
+}
+
+void Subprocess::KillAndWaitForTermination() {
+  kill(pid_, SIGKILL);
+  Wait();
+}
+
+std::string Subprocess::Args::GetCmdString() const {
+  std::string str;
+  for (size_t i = 0; i < exec_cmd.size(); i++) {
+    str += i > 0 ? " \"" : "";
+    str += exec_cmd[i];
+    str += i > 0 ? "\"" : "";
+  }
+  return str;
+}
+
+}  // namespace base
+}  // namespace perfetto
+
+#endif  // PERFETTO_HAS_SUBPROCESS()
diff --git a/src/base/subprocess_unittest.cc b/src/base/subprocess_unittest.cc
new file mode 100644
index 0000000..f31b360
--- /dev/null
+++ b/src/base/subprocess_unittest.cc
@@ -0,0 +1,269 @@
+/*
+ * 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/ext/base/subprocess.h"
+
+#if PERFETTO_HAS_SUBPROCESS()
+#include <thread>
+
+#include <signal.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/temp_file.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace base {
+namespace {
+
+std::string GenLargeString() {
+  std::string contents;
+  for (int i = 0; i < 4096; i++) {
+    contents += "very long text " + std::to_string(i) + "\n";
+  }
+  // Make sure that |contents| is > the default pipe buffer on Linux (4 pages).
+  PERFETTO_DCHECK(contents.size() > 4096 * 4);
+  return contents;
+}
+
+TEST(SubprocessTest, InvalidPath) {
+  Subprocess p({"/usr/bin/invalid_1337"});
+  EXPECT_FALSE(p.Call());
+  EXPECT_EQ(p.status(), Subprocess::kExited);
+  EXPECT_EQ(p.returncode(), 128);
+  EXPECT_EQ(p.output(), "execve() failed\n");
+}
+
+TEST(SubprocessTest, StdoutOnly) {
+  Subprocess p({"sh", "-c", "(echo skip_err >&2); echo out_only"});
+  p.args.stdout_mode = Subprocess::kBuffer;
+  p.args.stderr_mode = Subprocess::kDevNull;
+  EXPECT_TRUE(p.Call());
+  EXPECT_EQ(p.status(), Subprocess::kExited);
+  EXPECT_EQ(p.output(), "out_only\n");
+}
+
+TEST(SubprocessTest, StderrOnly) {
+  Subprocess p({"sh", "-c", "(echo err_only >&2); echo skip_out"});
+  p.args.stdout_mode = Subprocess::kDevNull;
+  p.args.stderr_mode = Subprocess::kBuffer;
+  EXPECT_TRUE(p.Call());
+  EXPECT_EQ(p.output(), "err_only\n");
+}
+
+TEST(SubprocessTest, BothStdoutAndStderr) {
+  Subprocess p({"sh", "-c", "echo out; (echo err >&2); echo out2"});
+  p.args.stdout_mode = Subprocess::kBuffer;
+  p.args.stderr_mode = Subprocess::kBuffer;
+  EXPECT_TRUE(p.Call());
+  EXPECT_EQ(p.output(), "out\nerr\nout2\n");
+}
+
+TEST(SubprocessTest, BinTrue) {
+  Subprocess p({"true"});
+  EXPECT_TRUE(p.Call());
+  EXPECT_EQ(p.status(), Subprocess::kExited);
+  EXPECT_EQ(p.returncode(), 0);
+}
+
+TEST(SubprocessTest, BinFalse) {
+  Subprocess p({"false"});
+  EXPECT_FALSE(p.Call());
+  EXPECT_EQ(p.status(), Subprocess::kExited);
+  EXPECT_EQ(p.returncode(), 1);
+}
+
+TEST(SubprocessTest, Echo) {
+  Subprocess p({"echo", "-n", "foobar"});
+  p.args.stdout_mode = Subprocess::kBuffer;
+  EXPECT_TRUE(p.Call());
+  EXPECT_EQ(p.status(), Subprocess::kExited);
+  EXPECT_EQ(p.returncode(), 0);
+  EXPECT_EQ(p.output(), "foobar");
+}
+
+TEST(SubprocessTest, FeedbackLongInput) {
+  std::string contents = GenLargeString();
+  Subprocess p({"cat", "-"});
+  p.args.stdout_mode = Subprocess::kBuffer;
+  p.args.input = contents;
+  EXPECT_TRUE(p.Call());
+  EXPECT_EQ(p.status(), Subprocess::kExited);
+  EXPECT_EQ(p.returncode(), 0);
+  EXPECT_EQ(p.output(), contents);
+}
+
+TEST(SubprocessTest, CatLargeFile) {
+  std::string contents = GenLargeString();
+  TempFile tf = TempFile::Create();
+  WriteAll(tf.fd(), contents.data(), contents.size());
+  FlushFile(tf.fd());
+  Subprocess p({"cat", tf.path().c_str()});
+  p.args.stdout_mode = Subprocess::kBuffer;
+  EXPECT_TRUE(p.Call());
+  EXPECT_EQ(p.output(), contents);
+}
+
+TEST(SubprocessTest, Timeout) {
+  Subprocess p({"sleep", "60"});
+  EXPECT_FALSE(p.Call(/*timeout_ms=*/1));
+  EXPECT_EQ(p.status(), Subprocess::kKilledBySignal);
+}
+
+TEST(SubprocessTest, TimeoutNotHit) {
+  Subprocess p({"sleep", "0.01"});
+  EXPECT_TRUE(p.Call(/*timeout_ms=*/100000));
+}
+
+TEST(SubprocessTest, TimeoutStopOutput) {
+  Subprocess p({"sh", "-c", "while true; do echo stuff; done"});
+  p.args.stdout_mode = Subprocess::kDevNull;
+  EXPECT_FALSE(p.Call(/*timeout_ms=*/10));
+  EXPECT_EQ(p.status(), Subprocess::kKilledBySignal);
+}
+
+TEST(SubprocessTest, ExitBeforeReadingStdin) {
+  // 'sh -c' is to avoid closing stdin (sleep closes it before sleeping).
+  Subprocess p({"sh", "-c", "sleep 0.01"});
+  p.args.stdout_mode = Subprocess::kDevNull;
+  p.args.stderr_mode = Subprocess::kDevNull;
+  p.args.input = GenLargeString();
+  EXPECT_TRUE(p.Call());
+  EXPECT_EQ(p.status(), Subprocess::kExited);
+  EXPECT_EQ(p.returncode(), 0);
+}
+
+TEST(SubprocessTest, StdinWriteStall) {
+  // 'sh -c' is to avoid closing stdin (sleep closes it before sleeping).
+  // This causes a situation where the write on the stdin will stall because
+  // nobody reads it and the pipe buffer fills up. In this situation we should
+  // still handle the timeout properly.
+  Subprocess p({"sh", "-c", "sleep 10"});
+  p.args.stdout_mode = Subprocess::kDevNull;
+  p.args.stderr_mode = Subprocess::kDevNull;
+  p.args.input = GenLargeString();
+  EXPECT_FALSE(p.Call(/*timeout_ms=*/10));
+  EXPECT_EQ(p.status(), Subprocess::kKilledBySignal);
+}
+
+TEST(SubprocessTest, StartAndWait) {
+  Subprocess p({"sleep", "1000"});
+  p.Start();
+  EXPECT_EQ(p.Poll(), Subprocess::kRunning);
+  p.KillAndWaitForTermination();
+  EXPECT_EQ(p.status(), Subprocess::kKilledBySignal);
+  EXPECT_EQ(p.Poll(), Subprocess::kKilledBySignal);
+  EXPECT_EQ(p.returncode(), 128 + SIGKILL);
+}
+
+TEST(SubprocessTest, PollBehavesProperly) {
+  Subprocess p({"sh", "-c", "echo foobar"});
+  p.args.stdout_mode = Subprocess::kBuffer;
+  p.args.input = "ignored";
+  p.Start();
+
+  // Here we use kill() as a way to tell if the process is still running.
+  // SIGWINCH is ignored by default.
+  while (kill(p.pid(), SIGWINCH) == 0) {
+    usleep(1000);
+  }
+
+  // At this point Poll() must detect the termination.
+  EXPECT_EQ(p.Poll(), Subprocess::kExited);
+  EXPECT_EQ(p.returncode(), 0);
+}
+
+// Test the case of passing a lambda in |entrypoint| but no cmd.c
+TEST(SubprocessTest, Entrypoint) {
+  Subprocess p;
+  p.args.input = "ping\n";
+  p.args.stdout_mode = Subprocess::kBuffer;
+  p.args.entrypoint_for_testing = [] {
+    char buf[32]{};
+    PERFETTO_CHECK(fgets(buf, sizeof(buf), stdin));
+    PERFETTO_CHECK(strcmp(buf, "ping\n") == 0);
+    printf("pong\n");
+    fflush(stdout);
+    _exit(42);
+  };
+  EXPECT_FALSE(p.Call());
+  EXPECT_EQ(p.returncode(), 42);
+  EXPECT_EQ(p.output(), "pong\n");
+}
+
+// Test the case of passing both a lambda entrypoint and a process to exec.
+TEST(SubprocessTest, EntrypointAndExec) {
+  base::Pipe pipe1 = base::Pipe::Create();
+  base::Pipe pipe2 = base::Pipe::Create();
+  int pipe1_wr = *pipe1.wr;
+  int pipe2_wr = *pipe2.wr;
+
+  Subprocess p({"echo", "123"});
+  p.args.stdout_mode = Subprocess::kBuffer;
+  p.args.preserve_fds.push_back(pipe2_wr);
+  p.args.entrypoint_for_testing = [pipe1_wr, pipe2_wr] {
+    base::ignore_result(write(pipe1_wr, "fail", 4));
+    base::ignore_result(write(pipe2_wr, "pass", 4));
+  };
+
+  p.Start();
+  pipe1.wr.reset();
+  pipe2.wr.reset();
+
+  char buf[8];
+  EXPECT_LE(read(*pipe1.rd, buf, sizeof(buf)), 0);
+  EXPECT_EQ(read(*pipe2.rd, buf, sizeof(buf)), 4);
+  buf[4] = '\0';
+  EXPECT_STREQ(buf, "pass");
+  EXPECT_TRUE(p.Wait());
+  EXPECT_EQ(p.status(), Subprocess::kExited);
+  EXPECT_EQ(p.output(), "123\n");
+}
+
+TEST(SubprocessTest, Wait) {
+  Subprocess p({"sleep", "10000"});
+  p.Start();
+  for (int i = 0; i < 3; i++) {
+    EXPECT_FALSE(p.Wait(1 /*ms*/));
+    EXPECT_EQ(p.status(), Subprocess::kRunning);
+  }
+  kill(p.pid(), SIGBUS);
+  EXPECT_TRUE(p.Wait(30000 /*ms*/));
+  EXPECT_TRUE(p.Wait());  // Should be a no-op.
+  EXPECT_EQ(p.status(), Subprocess::kKilledBySignal);
+  EXPECT_EQ(p.returncode(), 128 + SIGBUS);
+}
+
+TEST(SubprocessTest, KillOnDtor) {
+  // Here we use kill(SIGWINCH) as a way to tell if the process is still alive.
+  // SIGWINCH is one of the few signals that has default ignore disposition.
+  int pid;
+  {
+    Subprocess p({"sleep", "10000"});
+    p.Start();
+    pid = p.pid();
+    EXPECT_EQ(kill(pid, SIGWINCH), 0);
+  }
+  EXPECT_EQ(kill(pid, SIGWINCH), -1);
+}
+
+}  // namespace
+}  // namespace base
+}  // namespace perfetto
+
+#endif  // PERFETTO_HAS_SUBPROCESS()
diff --git a/src/base/test/utils.cc b/src/base/test/utils.cc
index 188038a..95c6e60 100644
--- a/src/base/test/utils.cc
+++ b/src/base/test/utils.cc
@@ -29,29 +29,50 @@
 #include <unistd.h>
 #endif
 
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) && \
+    !PERFETTO_BUILDFLAG(PERFETTO_COMPILER_GCC)
+#include <corecrt_io.h>
+#include <io.h>
+#endif
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
+#include <mach-o/dyld.h>
+#endif
+
 namespace perfetto {
 namespace base {
 
-std::string GetTestDataPath(const std::string& path) {
+std::string GetCurExecutableDir() {
+  std::string self_path;
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
     PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
   char buf[PATH_MAX];
-  ssize_t bytes = readlink("/proc/self/exe", buf, sizeof(buf));
-  PERFETTO_CHECK(bytes != -1);
+  ssize_t size = readlink("/proc/self/exe", buf, sizeof(buf));
+  PERFETTO_CHECK(size != -1);
   // readlink does not null terminate.
-  buf[bytes] = 0;
-  std::string self_path = std::string(buf);
+  self_path = std::string(buf, static_cast<size_t>(size));
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
+  uint32_t size = 0;
+  PERFETTO_CHECK(_NSGetExecutablePath(nullptr, &size));
+  self_path.resize(size);
+  PERFETTO_CHECK(_NSGetExecutablePath(&self_path[0], &size) == 0);
+#else
+  PERFETTO_FATAL(
+      "GetCurExecutableDir() not implemented on the current platform");
+#endif
   // Cut binary name.
-  self_path = self_path.substr(0, self_path.find_last_of("/"));
+  return self_path.substr(0, self_path.find_last_of("/"));
+}
+
+std::string GetTestDataPath(const std::string& path) {
+  std::string self_path = GetCurExecutableDir();
   std::string full_path = self_path + "/../../" + path;
-  if (access(full_path.c_str(), F_OK) == 0)
+  if (access(full_path.c_str(), 0 /*F_OK*/) == 0)
     return full_path;
   full_path = self_path + "/" + path;
-  if (access(full_path.c_str(), F_OK) == 0)
+  if (access(full_path.c_str(), 0 /*F_OK*/) == 0)
     return full_path;
-#endif
-  // TODO(hjd): Implement on MacOS/Windows
   // Fall back to relative to root dir.
   return path;
 }
diff --git a/src/base/test/utils.h b/src/base/test/utils.h
index 9f61eac..365c93f 100644
--- a/src/base/test/utils.h
+++ b/src/base/test/utils.h
@@ -50,6 +50,7 @@
 namespace perfetto {
 namespace base {
 
+std::string GetCurExecutableDir();
 std::string GetTestDataPath(const std::string& path);
 
 }  // namespace base
diff --git a/src/base/thread_task_runner.cc b/src/base/thread_task_runner.cc
index d1a3b93..bed6b72 100644
--- a/src/base/thread_task_runner.cc
+++ b/src/base/thread_task_runner.cc
@@ -25,6 +25,7 @@
 #include <thread>
 
 #include "perfetto/base/logging.h"
+#include "perfetto/ext/base/thread_utils.h"
 #include "perfetto/ext/base/unix_task_runner.h"
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
@@ -82,12 +83,7 @@
 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
+    base::MaybeSetThreadName(name_);
   }
 
   UnixTaskRunner task_runner;
diff --git a/src/base/unix_socket.cc b/src/base/unix_socket.cc
index f6db62a..3a92198 100644
--- a/src/base/unix_socket.cc
+++ b/src/base/unix_socket.cc
@@ -669,23 +669,16 @@
 bool UnixSocket::Send(const void* msg,
                       size_t len,
                       const int* send_fds,
-                      size_t num_fds,
-                      BlockingMode blocking_mode) {
-  // TODO(b/117139237): Non-blocking sends are broken because we do not
-  // properly handle partial sends.
-  PERFETTO_DCHECK(blocking_mode == BlockingMode::kBlocking);
-
+                      size_t num_fds) {
   if (state_ != State::kConnected) {
     errno = last_error_ = ENOTCONN;
     return false;
   }
 
-  if (blocking_mode == BlockingMode::kBlocking)
-    sock_raw_.SetBlocking(true);
+  sock_raw_.SetBlocking(true);
   const ssize_t sz = sock_raw_.Send(msg, len, send_fds, num_fds);
   int saved_errno = errno;
-  if (blocking_mode == BlockingMode::kBlocking)
-    sock_raw_.SetBlocking(false);
+  sock_raw_.SetBlocking(false);
 
   if (sz == static_cast<ssize_t>(len)) {
     last_error_ = 0;
diff --git a/src/base/unix_socket_unittest.cc b/src/base/unix_socket_unittest.cc
index 138f631..2a43200 100644
--- a/src/base/unix_socket_unittest.cc
+++ b/src/base/unix_socket_unittest.cc
@@ -45,7 +45,6 @@
 using ::testing::Mock;
 
 constexpr char kSocketName[] = TEST_SOCK_NAME("unix_socket_unittest");
-constexpr auto kBlocking = UnixSocket::BlockingMode::kBlocking;
 
 class MockEventListener : public UnixSocket::EventListener {
  public:
@@ -123,7 +122,7 @@
   auto cli_disconnected = task_runner_.CreateCheckpoint("cli_disconnected");
   EXPECT_CALL(event_listener_, OnDisconnect(cli.get()))
       .WillOnce(InvokeWithoutArgs(cli_disconnected));
-  EXPECT_FALSE(cli->Send("whatever", kBlocking));
+  EXPECT_FALSE(cli->Send("whatever"));
   task_runner_.RunUntilCheckpoint("cli_disconnected");
 }
 
@@ -163,8 +162,8 @@
         ASSERT_EQ("cli>srv", s->ReceiveString());
         srv_did_recv();
       }));
-  ASSERT_TRUE(cli->Send("cli>srv", kBlocking));
-  ASSERT_TRUE(srv_conn->Send("srv>cli", kBlocking));
+  ASSERT_TRUE(cli->Send("cli>srv"));
+  ASSERT_TRUE(srv_conn->Send("srv>cli"));
   task_runner_.RunUntilCheckpoint("cli_did_recv");
   task_runner_.RunUntilCheckpoint("srv_did_recv");
 
@@ -178,8 +177,8 @@
   ASSERT_EQ("", cli->ReceiveString());
   ASSERT_EQ(0u, srv_conn->Receive(&msg, sizeof(msg)));
   ASSERT_EQ("", srv_conn->ReceiveString());
-  ASSERT_FALSE(cli->Send("foo", kBlocking));
-  ASSERT_FALSE(srv_conn->Send("bar", kBlocking));
+  ASSERT_FALSE(cli->Send("foo"));
+  ASSERT_FALSE(srv_conn->Send("bar"));
   srv->Shutdown(true);
   task_runner_.RunUntilCheckpoint("cli_disconnected");
   task_runner_.RunUntilCheckpoint("srv_disconnected");
@@ -256,10 +255,10 @@
 
   int buf_fd[2] = {null_fd.get(), zero_fd.get()};
 
-  ASSERT_TRUE(cli->Send(cli_str, sizeof(cli_str), buf_fd,
-                        base::ArraySize(buf_fd), kBlocking));
+  ASSERT_TRUE(
+      cli->Send(cli_str, sizeof(cli_str), buf_fd, base::ArraySize(buf_fd)));
   ASSERT_TRUE(srv_conn->Send(srv_str, sizeof(srv_str), buf_fd,
-                             base::ArraySize(buf_fd), kBlocking));
+                             base::ArraySize(buf_fd)));
   task_runner_.RunUntilCheckpoint("srv_did_recv");
   task_runner_.RunUntilCheckpoint("cli_did_recv");
 
@@ -318,7 +317,7 @@
         EXPECT_CALL(event_listener_, OnDataAvailable(s))
             .WillOnce(Invoke([](UnixSocket* t) {
               ASSERT_EQ("PING", t->ReceiveString());
-              ASSERT_TRUE(t->Send("PONG", kBlocking));
+              ASSERT_TRUE(t->Send("PONG"));
             }));
       }));
 
@@ -328,7 +327,7 @@
     EXPECT_CALL(event_listener_, OnConnect(cli[i].get(), true))
         .WillOnce(Invoke([](UnixSocket* s, bool success) {
           ASSERT_TRUE(success);
-          ASSERT_TRUE(s->Send("PING", kBlocking));
+          ASSERT_TRUE(s->Send("PING"));
         }));
 
     auto checkpoint = task_runner_.CreateCheckpoint(std::to_string(i));
@@ -374,7 +373,7 @@
         .WillOnce(Invoke(
             [this, tmp_fd, checkpoint, mem](UnixSocket*, UnixSocket* new_conn) {
               ASSERT_EQ(geteuid(), static_cast<uint32_t>(new_conn->peer_uid()));
-              ASSERT_TRUE(new_conn->Send("txfd", 5, tmp_fd, kBlocking));
+              ASSERT_TRUE(new_conn->Send("txfd", 5, tmp_fd));
               // Wait for the client to change this again.
               EXPECT_CALL(event_listener_, OnDataAvailable(new_conn))
                   .WillOnce(Invoke([checkpoint, mem](UnixSocket* s) {
@@ -409,7 +408,7 @@
 
           // Now change the shared memory and ping the other process.
           memcpy(mem, "rock more", 10);
-          ASSERT_TRUE(s->Send("change notify", kBlocking));
+          ASSERT_TRUE(s->Send("change notify"));
           checkpoint();
         }));
     task_runner_.RunUntilCheckpoint("change_seen_by_client");
@@ -513,7 +512,7 @@
     char buf[1024 * 32] = {};
     tx_task_runner.PostTask([&cli, &buf, all_sent] {
       for (size_t i = 0; i < kTotalBytes / sizeof(buf); i++)
-        cli->Send(buf, sizeof(buf), -1 /*fd*/, kBlocking);
+        cli->Send(buf, sizeof(buf));
       all_sent();
     });
     tx_task_runner.RunUntilCheckpoint("all_sent", kTimeoutMs);
@@ -562,7 +561,7 @@
     static constexpr size_t kBufSize = 32 * 1024 * 1024;
     std::unique_ptr<char[]> buf(new char[kBufSize]());
     tx_task_runner.PostTask([&cli, &buf, send_done] {
-      bool send_res = cli->Send(buf.get(), kBufSize, -1 /*fd*/, kBlocking);
+      bool send_res = cli->Send(buf.get(), kBufSize);
       ASSERT_FALSE(send_res);
       send_done();
     });
@@ -754,7 +753,7 @@
   task_runner_.RunUntilCheckpoint("connected");
   srv->Shutdown(true);
 
-  cli->Send("test", UnixSocket::BlockingMode::kBlocking);
+  cli->Send("test");
 
   ASSERT_NE(peer, nullptr);
   auto raw_sock = peer->ReleaseSocket();
@@ -794,7 +793,7 @@
             .WillRepeatedly(Invoke([](UnixSocket* cli_sock) {
               cli_sock->ReceiveString();  // Read connection EOF;
             }));
-        ASSERT_TRUE(s->Send("welcome", kBlocking));
+        ASSERT_TRUE(s->Send("welcome"));
       }));
 
   for (size_t i = 0; i < kNumClients; i++) {
diff --git a/src/base/utils_unittest.cc b/src/base/utils_unittest.cc
index 5fcc27a..eade8f7 100644
--- a/src/base/utils_unittest.cc
+++ b/src/base/utils_unittest.cc
@@ -88,7 +88,7 @@
 
   char buf[6] = {};
   EXPECT_EQ(4, PERFETTO_EINTR(read(*pipe.rd, buf, sizeof(buf))));
-  EXPECT_EQ(0, PERFETTO_EINTR(close(*pipe.rd)));
+  EXPECT_TRUE(close(*pipe.rd) == 0 || errno == EINTR);
   pipe.wr.reset();
 
   // A 2nd close should fail with the proper errno.
diff --git a/src/base/watchdog_posix.cc b/src/base/watchdog_posix.cc
index 25782d9..2f251b7b 100644
--- a/src/base/watchdog_posix.cc
+++ b/src/base/watchdog_posix.cc
@@ -53,6 +53,32 @@
 
 }  //  namespace
 
+bool ReadProcStat(int fd, ProcStat* out) {
+  char c[512];
+  size_t c_pos = 0;
+  while (c_pos < sizeof(c) - 1) {
+    ssize_t rd = PERFETTO_EINTR(read(fd, c + c_pos, sizeof(c) - c_pos));
+    if (rd < 0) {
+      PERFETTO_ELOG("Failed to read stat file to enforce resource limits.");
+      return false;
+    }
+    if (rd == 0)
+      break;
+    c_pos += static_cast<size_t>(rd);
+  }
+  PERFETTO_CHECK(c_pos < sizeof(c));
+  c[c_pos] = '\0';
+
+  if (sscanf(c,
+             "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu"
+             "%lu %*d %*d %*d %*d %*d %*d %*u %*u %ld",
+             &out->utime, &out->stime, &out->rss_pages) != 3) {
+    PERFETTO_ELOG("Invalid stat format: %s", c);
+    return false;
+  }
+  return true;
+}
+
 Watchdog::Watchdog(uint32_t polling_interval_ms)
     : polling_interval_ms_(polling_interval_ms) {}
 
@@ -134,24 +160,14 @@
 
     lseek(stat_fd.get(), 0, SEEK_SET);
 
-    char c[512];
-    if (read(stat_fd.get(), c, sizeof(c)) < 0) {
-      PERFETTO_ELOG("Failed to read stat file to enforce resource limits.");
+    ProcStat stat;
+    if (!ReadProcStat(stat_fd.get(), &stat)) {
       return;
     }
-    c[sizeof(c) - 1] = '\0';
 
-    unsigned long int utime = 0l;
-    unsigned long int stime = 0l;
-    long int rss_pages = -1l;
-    PERFETTO_CHECK(
-        sscanf(c,
-               "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu"
-               "%lu %*d %*d %*d %*d %*d %*d %*u %*u %ld",
-               &utime, &stime, &rss_pages) == 3);
-
-    uint64_t cpu_time = utime + stime;
-    uint64_t rss_bytes = static_cast<uint64_t>(rss_pages) * base::kPageSize;
+    uint64_t cpu_time = stat.utime + stat.stime;
+    uint64_t rss_bytes =
+        static_cast<uint64_t>(stat.rss_pages) * base::kPageSize;
 
     CheckMemory(rss_bytes);
     CheckCpu(cpu_time);
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..c569eb4 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.
@@ -121,8 +123,7 @@
   // socket buffer is full? We might want to either drop the request or throttle
   // the send and PostTask the reply later? Right now we are making Send()
   // blocking as a workaround. Propagate bakpressure to the caller instead.
-  bool res = sock_->Send(buf.data(), buf.size(), fd,
-                         base::UnixSocket::BlockingMode::kBlocking);
+  bool res = sock_->Send(buf.data(), buf.size(), fd);
   PERFETTO_CHECK(res || !sock_->is_connected());
   return res;
 }
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..183fbf0 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 {
@@ -186,8 +187,7 @@
   void Reply(const Frame& frame) {
     auto buf = BufferedFrameDeserializer::Serialize(frame);
     ASSERT_TRUE(client_sock->is_connected());
-    EXPECT_TRUE(client_sock->Send(buf.data(), buf.size(), next_reply_fd,
-                                  base::UnixSocket::BlockingMode::kBlocking));
+    EXPECT_TRUE(client_sock->Send(buf.data(), buf.size(), next_reply_fd));
     next_reply_fd = -1;
   }
 
diff --git a/src/ipc/host_impl.cc b/src/ipc/host_impl.cc
index 8635f22..2806a53 100644
--- a/src/ipc/host_impl.cc
+++ b/src/ipc/host_impl.cc
@@ -236,8 +236,7 @@
   // socket buffer is full? We might want to either drop the request or throttle
   // the send and PostTask the reply later? Right now we are making Send()
   // blocking as a workaround. Propagate bakpressure to the caller instead.
-  bool res = client->sock->Send(buf.data(), buf.size(), fd,
-                                base::UnixSocket::BlockingMode::kBlocking);
+  bool res = client->sock->Send(buf.data(), buf.size(), fd);
   PERFETTO_CHECK(res || !client->sock->is_connected());
 }
 
diff --git a/src/ipc/host_impl_unittest.cc b/src/ipc/host_impl_unittest.cc
index 1222bd7..d030a02 100644
--- a/src/ipc/host_impl_unittest.cc
+++ b/src/ipc/host_impl_unittest.cc
@@ -156,8 +156,7 @@
 
   void SendFrame(const Frame& frame, int fd = -1) {
     std::string buf = BufferedFrameDeserializer::Serialize(frame);
-    ASSERT_TRUE(sock_->Send(buf.data(), buf.size(), fd,
-                            base::UnixSocket::BlockingMode::kBlocking));
+    ASSERT_TRUE(sock_->Send(buf.data(), buf.size(), fd));
   }
 
   BufferedFrameDeserializer frame_deserializer_;
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/perfetto_cmd/BUILD.gn b/src/perfetto_cmd/BUILD.gn
index 6e920a9..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
@@ -67,7 +61,7 @@
     "../android_internal:lazy_library_loader",
     "../base",
     "../protozero",
-    "../tracing:ipc",
+    "../tracing/ipc/consumer",
   ]
   if (enable_perfetto_zlib) {
     deps += [ "../../gn:zlib" ]
@@ -99,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") {
@@ -114,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
 }
 
@@ -138,7 +128,7 @@
     "../../protos/perfetto/config:cpp",
     "../../protos/perfetto/config/ftrace:cpp",
     "../../protos/perfetto/trace:cpp",
-    "../tracing",
+    "../tracing/core",
   ]
   if (enable_perfetto_zlib) {
     deps += [ "../../gn:zlib" ]
diff --git a/src/perfetto_cmd/packet_writer.cc b/src/perfetto_cmd/packet_writer.cc
index 2fb0562..4897469 100644
--- a/src/perfetto_cmd/packet_writer.cc
+++ b/src/perfetto_cmd/packet_writer.cc
@@ -53,7 +53,11 @@
 // ID of |compressed_packets| in trace_packet.proto.
 constexpr uint32_t kCompressedPacketsId = 50;
 
-// Maximum allowable size for a single packet.
+// Some transport mechanisms have a 512kb limit on packet size.
+// ZipPacketWriter respects this limit where possible and does
+// not produce compressed packets larger than 512kb. This is
+// constant is deliberately conservative to leave plenty of
+// room for the transport to add additional headers etc.
 const size_t kMaxPacketSize = 500 * 1024;
 
 // After every kPendingBytesLimit we do a Z_SYNC_FLUSH in the zlib stream.
@@ -77,7 +81,7 @@
  public:
   FilePacketWriter(FILE* fd);
   ~FilePacketWriter() override;
-  bool WritePackets(const std::vector<TracePacket>& packets) override;
+  bool WritePacket(const TracePacket& packet) override;
 
  private:
   FILE* fd_;
@@ -89,17 +93,15 @@
   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)
+bool FilePacketWriter::WritePacket(const TracePacket& packet) {
+  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;
-    for (const Slice& slice : packet.slices()) {
-      if (fwrite(reinterpret_cast<const char*>(slice.start), 1, slice.size,
-                 fd_) != slice.size) {
-        return false;
-      }
     }
   }
 
@@ -112,10 +114,9 @@
  public:
   ZipPacketWriter(std::unique_ptr<PacketWriter>);
   ~ZipPacketWriter() override;
-  bool WritePackets(const std::vector<TracePacket>& packets) override;
+  bool WritePacket(const TracePacket& packet) override;
 
  private:
-  bool WritePacket(const TracePacket& packet);
   void CheckEq(int actual_code, int expected_code);
   bool FinalizeCompressedPacket();
   inline void Deflate(const char* ptr, size_t size) {
@@ -148,14 +149,6 @@
     FinalizeCompressedPacket();
 }
 
-bool ZipPacketWriter::WritePackets(const std::vector<TracePacket>& packets) {
-  for (const TracePacket& packet : packets) {
-    if (!WritePacket(packet))
-      return false;
-  }
-  return true;
-}
-
 bool ZipPacketWriter::WritePacket(const TracePacket& packet) {
   // If we have already written one compressed packet, check whether we should
   // flush the buffer.
@@ -188,10 +181,19 @@
     }
   }
 
+  // Do not attempt to compress large packets (since they may overflow our
+  // output buffer) instead insert them directly into the output stream:
+  if (packet.size() > kMaxPacketSize) {
+    // We can't be compressing here, if we were we would have attempted
+    // to FinalizeCompressedPacket() above:
+    PERFETTO_DCHECK(!is_compressing_);
+    return writer_->WritePacket(packet);
+  }
+
   // 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_);
diff --git a/src/perfetto_cmd/packet_writer.h b/src/perfetto_cmd/packet_writer.h
index 11d4f29..e42d1e9 100644
--- a/src/perfetto_cmd/packet_writer.h
+++ b/src/perfetto_cmd/packet_writer.h
@@ -22,15 +22,23 @@
 
 #include <stdio.h>
 
-namespace perfetto {
+#include "perfetto/ext/tracing/core/trace_packet.h"
 
-class TracePacket;
+namespace perfetto {
 
 class PacketWriter {
  public:
   PacketWriter();
   virtual ~PacketWriter();
-  virtual bool WritePackets(const std::vector<TracePacket>& packets) = 0;
+  virtual bool WritePackets(const std::vector<TracePacket>& packets) {
+    for (const TracePacket& packet : packets) {
+      if (!WritePacket(packet)) {
+        return false;
+      }
+    }
+    return true;
+  }
+  virtual bool WritePacket(const TracePacket& packets) = 0;
 };
 
 std::unique_ptr<PacketWriter> CreateFilePacketWriter(FILE*);
diff --git a/src/perfetto_cmd/packet_writer_unittest.cc b/src/perfetto_cmd/packet_writer_unittest.cc
index fbf9603..fb64f50 100644
--- a/src/perfetto_cmd/packet_writer_unittest.cc
+++ b/src/perfetto_cmd/packet_writer_unittest.cc
@@ -55,6 +55,16 @@
 }
 
 #if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+std::string RandomString(size_t size) {
+  std::minstd_rand0 rnd(0);
+  std::uniform_int_distribution<> dist(0, 255);
+  std::string s;
+  s.resize(size);
+  for (size_t i = 0; i < s.size(); i++)
+    s[i] = static_cast<char>(dist(rnd));
+  return s;
+}
+
 std::string Decompress(const std::string& data) {
   uint8_t out[1024];
 
@@ -83,20 +93,18 @@
 #endif  // PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
 
 TEST(PacketWriterTest, FilePacketWriter) {
-  base::TempFile tmp = base::TempFile::Create();
+  base::TempFile tmp = base::TempFile::CreateUnlinked();
   base::ScopedResource<FILE*, fclose, nullptr> f(
       fdopen(tmp.ReleaseFD().release(), "wb"));
 
+  std::vector<perfetto::TracePacket> packets;
+  packets.push_back(CreateTracePacket([](TracePacketProto* msg) {
+    auto* for_testing = msg->mutable_for_testing();
+    for_testing->set_str("abc");
+  }));
+
   {
     std::unique_ptr<PacketWriter> writer = CreateFilePacketWriter(*f);
-
-    std::vector<perfetto::TracePacket> packets;
-
-    packets.push_back(CreateTracePacket([](TracePacketProto* msg) {
-      auto* for_testing = msg->mutable_for_testing();
-      for_testing->set_str("abc");
-    }));
-
     EXPECT_TRUE(writer->WritePackets(std::move(packets)));
   }
 
@@ -113,23 +121,21 @@
 #if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
 
 TEST(PacketWriterTest, ZipPacketWriter) {
-  base::TempFile tmp = base::TempFile::Create();
+  base::TempFile tmp = base::TempFile::CreateUnlinked();
   base::ScopedResource<FILE*, fclose, nullptr> f(
       fdopen(tmp.ReleaseFD().release(), "wb"));
 
-  {
-    std::unique_ptr<PacketWriter> writer =
-        CreateZipPacketWriter(CreateFilePacketWriter(*f));
-
     std::vector<perfetto::TracePacket> packets;
-
     packets.push_back(CreateTracePacket([](TracePacketProto* msg) {
       auto* for_testing = msg->mutable_for_testing();
       for_testing->set_str("abc");
     }));
 
-    EXPECT_TRUE(writer->WritePackets(std::move(packets)));
-  }
+    {
+      std::unique_ptr<PacketWriter> writer =
+          CreateZipPacketWriter(CreateFilePacketWriter(*f));
+      EXPECT_TRUE(writer->WritePackets(std::move(packets)));
+    }
 
   std::string s;
   fseek(*f, 0, SEEK_SET);
@@ -148,7 +154,7 @@
 }
 
 TEST(PacketWriterTest, ZipPacketWriter_Empty) {
-  base::TempFile tmp = base::TempFile::Create();
+  base::TempFile tmp = base::TempFile::CreateUnlinked();
   base::ScopedResource<FILE*, fclose, nullptr> f(
       fdopen(tmp.ReleaseFD().release(), "wb"));
 
@@ -161,7 +167,7 @@
 }
 
 TEST(PacketWriterTest, ZipPacketWriter_EmptyWithEmptyWrite) {
-  base::TempFile tmp = base::TempFile::Create();
+  base::TempFile tmp = base::TempFile::CreateUnlinked();
   base::ScopedResource<FILE*, fclose, nullptr> f(
       fdopen(tmp.ReleaseFD().release(), "wb"));
 
@@ -177,33 +183,31 @@
 }
 
 TEST(PacketWriterTest, ZipPacketWriter_ShouldCompress) {
-  base::TempFile tmp = base::TempFile::Create();
+  base::TempFile tmp = base::TempFile::CreateUnlinked();
   base::ScopedResource<FILE*, fclose, nullptr> f(
       fdopen(tmp.ReleaseFD().release(), "wb"));
   size_t uncompressed_size = 0;
 
+  std::vector<perfetto::TracePacket> packets;
+  for (size_t i = 0; i < 200; i++) {
+    packets.push_back(CreateTracePacket([](TracePacketProto* msg) {
+      auto* for_testing = msg->mutable_for_testing();
+      for_testing->set_str("abcdefghijklmn");
+    }));
+
+    packets.push_back(CreateTracePacket([](TracePacketProto* msg) {
+      auto* for_testing = msg->mutable_for_testing();
+      for_testing->set_str("abcdefghijklmn");
+    }));
+
+    for (const TracePacket& packet : packets)
+      uncompressed_size += packet.size();
+  }
+
   {
     std::unique_ptr<PacketWriter> writer =
         CreateZipPacketWriter(CreateFilePacketWriter(*f));
-
-    for (size_t i = 0; i < 200; i++) {
-      std::vector<perfetto::TracePacket> packets;
-
-      packets.push_back(CreateTracePacket([](TracePacketProto* msg) {
-        auto* for_testing = msg->mutable_for_testing();
-        for_testing->set_str("abcdefghijklmn");
-      }));
-
-      packets.push_back(CreateTracePacket([](TracePacketProto* msg) {
-        auto* for_testing = msg->mutable_for_testing();
-        for_testing->set_str("abcdefghijklmn");
-      }));
-
-      for (const TracePacket& packet : packets)
-        uncompressed_size += packet.size();
-
-      EXPECT_TRUE(writer->WritePackets(std::move(packets)));
-    }
+    EXPECT_TRUE(writer->WritePackets(std::move(packets)));
   }
 
   std::string s;
@@ -231,38 +235,62 @@
   EXPECT_EQ(packet_count, 200 * 2u);
 }
 
-TEST(PacketWriterTest, ZipPacketWriter_ShouldSplitPackets) {
-  base::TempFile tmp = base::TempFile::Create();
+TEST(PacketWriterTest, ZipPacketWriter_LargePacket) {
+  base::TempFile tmp = base::TempFile::CreateUnlinked();
   base::ScopedResource<FILE*, fclose, nullptr> f(
       fdopen(tmp.ReleaseFD().release(), "wb"));
 
-  std::minstd_rand0 rnd(0);
-  std::uniform_int_distribution<> dist(0, 255);
-  auto randomString = [&dist, &rnd]() {
-    std::string s;
-    s.resize(1024);
-    for (size_t i = 0; i < s.size(); i++)
-      s[i] = static_cast<char>(dist(rnd));
-    return s;
+  std::vector<perfetto::TracePacket> packets;
+
+  auto add_packet = [&packets](size_t size) {
+    std::string s = RandomString(size);
+    packets.push_back(CreateTracePacket([&s](TracePacketProto* msg) {
+      auto* for_testing = msg->mutable_for_testing();
+      for_testing->set_str(s);
+    }));
   };
 
+  add_packet(1 * 1024 * 1024);
+  add_packet(10);
+  add_packet(1 * 1024 * 1024);
+  add_packet(1 * 1024);
+  add_packet(1 * 1024);
+  add_packet(2 * 1024 * 1024);
+
   {
     std::unique_ptr<PacketWriter> writer =
         CreateZipPacketWriter(CreateFilePacketWriter(*f));
+    EXPECT_TRUE(writer->WritePackets(std::move(packets)));
+  }
 
-    for (uint32_t i = 0; i < 1000; i++) {
-      std::vector<perfetto::TracePacket> packets;
+  std::string s;
+  fseek(*f, 0, SEEK_SET);
+  EXPECT_TRUE(base::ReadFileStream(*f, &s));
+  EXPECT_GT(s.size(), 0u);
 
-      std::string s = randomString();
+  protos::gen::Trace trace;
+  EXPECT_TRUE(trace.ParseFromString(s));
+}
 
-      packets.push_back(CreateTracePacket([i, &s](TracePacketProto* msg) {
-        auto* for_testing = msg->mutable_for_testing();
-        for_testing->set_seq_value(i);
-        for_testing->set_str(s);
-      }));
+TEST(PacketWriterTest, ZipPacketWriter_ShouldSplitPackets) {
+  base::TempFile tmp = base::TempFile::CreateUnlinked();
+  base::ScopedResource<FILE*, fclose, nullptr> f(
+      fdopen(tmp.ReleaseFD().release(), "wb"));
 
-      EXPECT_TRUE(writer->WritePackets(std::move(packets)));
-    }
+  std::vector<perfetto::TracePacket> packets;
+
+  for (uint32_t i = 0; i < 1000; i++) {
+    packets.push_back(CreateTracePacket([i](TracePacketProto* msg) {
+      auto* for_testing = msg->mutable_for_testing();
+      for_testing->set_seq_value(i);
+      for_testing->set_str(RandomString(1024));
+    }));
+  }
+
+  {
+    std::unique_ptr<PacketWriter> writer =
+        CreateZipPacketWriter(CreateFilePacketWriter(*f));
+    EXPECT_TRUE(writer->WritePackets(std::move(packets)));
   }
 
   std::string s;
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index bb48f6f..bdfef73 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -38,6 +38,7 @@
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/string_view.h"
+#include "perfetto/ext/base/thread_utils.h"
 #include "perfetto/ext/base/utils.h"
 #include "perfetto/ext/base/uuid.h"
 #include "perfetto/ext/traced/traced.h"
@@ -182,7 +183,7 @@
   --detach=key          : Detach from the tracing session with the given key.
   --attach=key [--stop] : Re-attach to the session (optionally stop tracing once reattached).
   --is_detached=key     : Check if the session can be re-attached (0:Yes, 2:No, 1:Error).
-)",
+)", /* this comment fixes syntax highlighting in some editors */
                 argv0);
   return 1;
 }
@@ -494,7 +495,8 @@
 
   if (!trace_config_->incident_report_config().destination_package().empty()) {
     if (dropbox_tag_.empty()) {
-      PERFETTO_ELOG("Unexpected IncidentReportConfig without --dropbox.");
+      PERFETTO_ELOG(
+          "Unexpected IncidentReportConfig without --dropbox / --upload.");
       return 1;
     }
   }
@@ -509,6 +511,23 @@
     return 1;
   }
 
+  if (!trace_config_->output_path().empty()) {
+    if (!trace_out_path_.empty() || !dropbox_tag_.empty()) {
+      PERFETTO_ELOG(
+          "Can't pass --out or --dropbox if output_path is set in the "
+          "trace config");
+      return 1;
+    }
+    if (access(trace_config_->output_path().c_str(), F_OK) == 0) {
+      PERFETTO_ELOG(
+          "The output_path must not exist, the service cannot overwrite "
+          "existing files for security reasons. Remove %s or use a different "
+          "path.",
+          trace_config_->output_path().c_str());
+      return 1;
+    }
+  }
+
   // |activate_triggers| in the trace config is shorthand for trigger_perfetto.
   // In this case we don't intend to send any trace config to the service,
   // rather use that as a signal to the cmdline client to connect as a producer
@@ -527,7 +546,9 @@
       PERFETTO_ELOG("Can't pass an --out file (or --dropbox) with this option");
       return 1;
     }
-  } else if (!triggers_to_activate.empty()) {
+  } else if (!triggers_to_activate.empty() ||
+             (trace_config_->write_into_file() &&
+              !trace_config_->output_path().empty())) {
     open_out_file = false;
   } else if (trace_out_path_.empty() && dropbox_tag_.empty()) {
     PERFETTO_ELOG("Either --out or --dropbox is required");
@@ -626,6 +647,9 @@
       trace_config_->guardrail_overrides().max_upload_per_day_bytes();
 #endif
 
+  if (!args.unique_session_name.empty())
+    base::MaybeSetThreadName("p-" + args.unique_session_name);
+
   if (args.is_dropbox && !args.ignore_guardrails &&
       (trace_config_->duration_ms() == 0 &&
        trace_config_->trigger_config().trigger_timeout_ms() == 0)) {
@@ -691,7 +715,7 @@
   trace_config_->set_enable_extra_guardrails(!dropbox_tag_.empty());
 
   base::ScopedFile optional_fd;
-  if (trace_config_->write_into_file())
+  if (trace_config_->write_into_file() && trace_config_->output_path().empty())
     optional_fd.reset(dup(fileno(*trace_out_stream_)));
 
   consumer_endpoint_->EnableTracing(*trace_config_, std::move(optional_fd));
diff --git a/src/perfetto_cmd/perfetto_config.descriptor.h b/src/perfetto_cmd/perfetto_config.descriptor.h
index f9e012e..004e74c 100644
--- a/src/perfetto_cmd/perfetto_config.descriptor.h
+++ b/src/perfetto_cmd/perfetto_config.descriptor.h
@@ -25,355 +25,260 @@
 // This file was autogenerated by tools/gen_binary_descriptors. Do not edit.
 
 // SHA1(tools/gen_binary_descriptors)
-// d6628b15181dba5287e35b56b966b39ea93d42b1
+// 3df80477da2ea38cc659967487b37051a154bb69
 // SHA1(protos/perfetto/config/perfetto_config.proto)
-// 9bf7f741997f67724c7ee1b186b117708ac5228d
+// 68347f3c41a0a3523dea874bf458345ffb055109
 
 // This is the proto PerfettoConfig encoded as a ProtoFileDescriptor to allow
 // for reflection without libprotobuf full/non-lite protos.
 
 namespace perfetto {
 
-constexpr std::array<uint8_t, 17225> kPerfettoConfigDescriptor{
-    {0x0a, 0xc5, 0x86, 0x01, 0x0a, 0x2c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+constexpr std::array<uint8_t, 17304> kPerfettoConfigDescriptor{
+    {0x0a, 0x94, 0x87, 0x01, 0x0a, 0x2c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
      0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x63, 0x6f,
      0x6e, 0x66, 0x69, 0x67, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
      0x6f, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f,
      0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0x93, 0x03, 0x0a, 0x14,
-     0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65,
-     0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04,
-     0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
-     0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x13, 0x77, 0x69, 0x6c, 0x6c,
-     0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x6f, 0x6e, 0x5f, 0x73,
-     0x74, 0x6f, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x77,
-     0x69, 0x6c, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4f, 0x6e, 0x53,
-     0x74, 0x6f, 0x70, 0x12, 0x2f, 0x0a, 0x14, 0x77, 0x69, 0x6c, 0x6c, 0x5f,
-     0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x6f, 0x6e, 0x5f, 0x73, 0x74,
-     0x61, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x77,
-     0x69, 0x6c, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4f, 0x6e, 0x53,
-     0x74, 0x61, 0x72, 0x74, 0x12, 0x45, 0x0a, 0x1f, 0x68, 0x61, 0x6e, 0x64,
-     0x6c, 0x65, 0x73, 0x5f, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e,
-     0x74, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x6c,
-     0x65, 0x61, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x68,
-     0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d,
-     0x65, 0x6e, 0x74, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x6c,
-     0x65, 0x61, 0x72, 0x12, 0x5f, 0x0a, 0x16, 0x67, 0x70, 0x75, 0x5f, 0x63,
-     0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72,
-     0x69, 0x70, 0x74, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32,
-     0x25, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0xf7, 0x0b, 0x0a, 0x14,
+     0x47, 0x70, 0x75, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x44, 0x65,
+     0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x4a, 0x0a, 0x05,
+     0x73, 0x70, 0x65, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
+     0x34, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
      0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x47, 0x70, 0x75, 0x43, 0x6f, 0x75,
      0x6e, 0x74, 0x65, 0x72, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
-     0x6f, 0x72, 0x42, 0x02, 0x28, 0x01, 0x52, 0x14, 0x67, 0x70, 0x75, 0x43,
-     0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69,
-     0x70, 0x74, 0x6f, 0x72, 0x12, 0x5f, 0x0a, 0x16, 0x74, 0x72, 0x61, 0x63,
-     0x6b, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x64, 0x65, 0x73, 0x63,
-     0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b,
-     0x32, 0x25, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x6b,
-     0x45, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
-     0x74, 0x6f, 0x72, 0x42, 0x02, 0x28, 0x01, 0x52, 0x14, 0x74, 0x72, 0x61,
-     0x63, 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x73, 0x63, 0x72,
-     0x69, 0x70, 0x74, 0x6f, 0x72, 0x22, 0xf7, 0x0b, 0x0a, 0x14, 0x47, 0x70,
-     0x75, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x44, 0x65, 0x73, 0x63,
-     0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x4a, 0x0a, 0x05, 0x73, 0x70,
-     0x65, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e,
-     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x73, 0x2e, 0x47, 0x70, 0x75, 0x43, 0x6f, 0x75, 0x6e, 0x74,
-     0x65, 0x72, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72,
-     0x2e, 0x47, 0x70, 0x75, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x53,
-     0x70, 0x65, 0x63, 0x52, 0x05, 0x73, 0x70, 0x65, 0x63, 0x73, 0x12, 0x4d,
-     0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x03,
-     0x28, 0x0b, 0x32, 0x35, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x72, 0x2e, 0x47, 0x70, 0x75, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65,
+     0x72, 0x53, 0x70, 0x65, 0x63, 0x52, 0x05, 0x73, 0x70, 0x65, 0x63, 0x73,
+     0x12, 0x4d, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x02,
+     0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
+     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x47,
+     0x70, 0x75, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x44, 0x65, 0x73,
+     0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x47, 0x70, 0x75, 0x43,
+     0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52,
+     0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x33, 0x0a, 0x16, 0x6d,
+     0x69, 0x6e, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x5f,
+     0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x6e, 0x73, 0x18, 0x03, 0x20,
+     0x01, 0x28, 0x04, 0x52, 0x13, 0x6d, 0x69, 0x6e, 0x53, 0x61, 0x6d, 0x70,
+     0x6c, 0x69, 0x6e, 0x67, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x4e, 0x73,
+     0x12, 0x33, 0x0a, 0x16, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x61, 0x6d, 0x70,
+     0x6c, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f,
+     0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x13, 0x6d, 0x61,
+     0x78, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x50, 0x65, 0x72,
+     0x69, 0x6f, 0x64, 0x4e, 0x73, 0x12, 0x44, 0x0a, 0x1e, 0x73, 0x75, 0x70,
+     0x70, 0x6f, 0x72, 0x74, 0x73, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75,
+     0x6d, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c,
+     0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x73,
+     0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x49, 0x6e, 0x73, 0x74, 0x72,
+     0x75, 0x6d, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x53, 0x61, 0x6d, 0x70, 0x6c,
+     0x69, 0x6e, 0x67, 0x1a, 0xb7, 0x03, 0x0a, 0x0e, 0x47, 0x70, 0x75, 0x43,
+     0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x1d,
+     0x0a, 0x0a, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64,
+     0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x63, 0x6f, 0x75, 0x6e,
+     0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
+     0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
+     0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+     0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
+     0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12,
+     0x26, 0x0a, 0x0e, 0x69, 0x6e, 0x74, 0x5f, 0x70, 0x65, 0x61, 0x6b, 0x5f,
+     0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x48,
+     0x00, 0x52, 0x0c, 0x69, 0x6e, 0x74, 0x50, 0x65, 0x61, 0x6b, 0x56, 0x61,
+     0x6c, 0x75, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x64, 0x6f, 0x75, 0x62, 0x6c,
+     0x65, 0x5f, 0x70, 0x65, 0x61, 0x6b, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65,
+     0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x0f, 0x64, 0x6f,
+     0x75, 0x62, 0x6c, 0x65, 0x50, 0x65, 0x61, 0x6b, 0x56, 0x61, 0x6c, 0x75,
+     0x65, 0x12, 0x5a, 0x0a, 0x0f, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x74,
+     0x6f, 0x72, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03,
+     0x28, 0x0e, 0x32, 0x31, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
      0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x47, 0x70, 0x75,
      0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x44, 0x65, 0x73, 0x63, 0x72,
-     0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x47, 0x70, 0x75, 0x43, 0x6f, 0x75,
-     0x6e, 0x74, 0x65, 0x72, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62,
-     0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x33, 0x0a, 0x16, 0x6d, 0x69, 0x6e,
-     0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x65,
-     0x72, 0x69, 0x6f, 0x64, 0x5f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
-     0x04, 0x52, 0x13, 0x6d, 0x69, 0x6e, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69,
-     0x6e, 0x67, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x4e, 0x73, 0x12, 0x33,
-     0x0a, 0x16, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69,
-     0x6e, 0x67, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x6e, 0x73,
-     0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x13, 0x6d, 0x61, 0x78, 0x53,
-     0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x50, 0x65, 0x72, 0x69, 0x6f,
-     0x64, 0x4e, 0x73, 0x12, 0x44, 0x0a, 0x1e, 0x73, 0x75, 0x70, 0x70, 0x6f,
-     0x72, 0x74, 0x73, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x6d, 0x65,
-     0x6e, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e,
-     0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x73, 0x75, 0x70,
-     0x70, 0x6f, 0x72, 0x74, 0x73, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x6d,
-     0x65, 0x6e, 0x74, 0x65, 0x64, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e,
-     0x67, 0x1a, 0xb7, 0x03, 0x0a, 0x0e, 0x47, 0x70, 0x75, 0x43, 0x6f, 0x75,
-     0x6e, 0x74, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x1d, 0x0a, 0x0a,
-     0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01,
-     0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65,
-     0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
-     0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
-     0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
-     0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65,
-     0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a,
-     0x0e, 0x69, 0x6e, 0x74, 0x5f, 0x70, 0x65, 0x61, 0x6b, 0x5f, 0x76, 0x61,
-     0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52,
-     0x0c, 0x69, 0x6e, 0x74, 0x50, 0x65, 0x61, 0x6b, 0x56, 0x61, 0x6c, 0x75,
-     0x65, 0x12, 0x2c, 0x0a, 0x11, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x5f,
-     0x70, 0x65, 0x61, 0x6b, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x06,
-     0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x0f, 0x64, 0x6f, 0x75, 0x62,
-     0x6c, 0x65, 0x50, 0x65, 0x61, 0x6b, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12,
-     0x5a, 0x0a, 0x0f, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72,
-     0x5f, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0e,
+     0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72,
+     0x65, 0x55, 0x6e, 0x69, 0x74, 0x52, 0x0e, 0x6e, 0x75, 0x6d, 0x65, 0x72,
+     0x61, 0x74, 0x6f, 0x72, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x12, 0x5e, 0x0a,
+     0x11, 0x64, 0x65, 0x6e, 0x6f, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72,
+     0x5f, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0e,
      0x32, 0x31, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
      0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x47, 0x70, 0x75, 0x43, 0x6f,
      0x75, 0x6e, 0x74, 0x65, 0x72, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
      0x74, 0x6f, 0x72, 0x2e, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x55,
-     0x6e, 0x69, 0x74, 0x52, 0x0e, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x74,
-     0x6f, 0x72, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x12, 0x5e, 0x0a, 0x11, 0x64,
-     0x65, 0x6e, 0x6f, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x75,
-     0x6e, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x31,
-     0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x47, 0x70, 0x75, 0x43, 0x6f, 0x75, 0x6e,
-     0x74, 0x65, 0x72, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f,
-     0x72, 0x2e, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x55, 0x6e, 0x69,
-     0x74, 0x52, 0x10, 0x64, 0x65, 0x6e, 0x6f, 0x6d, 0x69, 0x6e, 0x61, 0x74,
-     0x6f, 0x72, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x73,
-     0x65, 0x6c, 0x65, 0x63, 0x74, 0x5f, 0x62, 0x79, 0x5f, 0x64, 0x65, 0x66,
-     0x61, 0x75, 0x6c, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f,
-     0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x42, 0x79, 0x44, 0x65, 0x66, 0x61,
-     0x75, 0x6c, 0x74, 0x42, 0x0c, 0x0a, 0x0a, 0x70, 0x65, 0x61, 0x6b, 0x5f,
-     0x76, 0x61, 0x6c, 0x75, 0x65, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x1a,
-     0xaa, 0x01, 0x0a, 0x0f, 0x47, 0x70, 0x75, 0x43, 0x6f, 0x75, 0x6e, 0x74,
-     0x65, 0x72, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x62,
-     0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
-     0x0d, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x25,
-     0x0a, 0x0e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x61, 0x70, 0x61,
-     0x63, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d,
-     0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74,
-     0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20,
-     0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a,
-     0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
-     0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63,
-     0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x63,
-     0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05,
-     0x20, 0x03, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65,
-     0x72, 0x49, 0x64, 0x73, 0x22, 0xac, 0x04, 0x0a, 0x0b, 0x4d, 0x65, 0x61,
-     0x73, 0x75, 0x72, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x12, 0x08, 0x0a, 0x04,
-     0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x42, 0x49,
-     0x54, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x49, 0x4c, 0x4f, 0x42,
-     0x49, 0x54, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x4d, 0x45, 0x47, 0x41,
-     0x42, 0x49, 0x54, 0x10, 0x03, 0x12, 0x0b, 0x0a, 0x07, 0x47, 0x49, 0x47,
-     0x41, 0x42, 0x49, 0x54, 0x10, 0x04, 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x45,
-     0x52, 0x41, 0x42, 0x49, 0x54, 0x10, 0x05, 0x12, 0x0b, 0x0a, 0x07, 0x50,
-     0x45, 0x54, 0x41, 0x42, 0x49, 0x54, 0x10, 0x06, 0x12, 0x08, 0x0a, 0x04,
-     0x42, 0x59, 0x54, 0x45, 0x10, 0x07, 0x12, 0x0c, 0x0a, 0x08, 0x4b, 0x49,
-     0x4c, 0x4f, 0x42, 0x59, 0x54, 0x45, 0x10, 0x08, 0x12, 0x0c, 0x0a, 0x08,
-     0x4d, 0x45, 0x47, 0x41, 0x42, 0x59, 0x54, 0x45, 0x10, 0x09, 0x12, 0x0c,
-     0x0a, 0x08, 0x47, 0x49, 0x47, 0x41, 0x42, 0x59, 0x54, 0x45, 0x10, 0x0a,
-     0x12, 0x0c, 0x0a, 0x08, 0x54, 0x45, 0x52, 0x41, 0x42, 0x59, 0x54, 0x45,
-     0x10, 0x0b, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x45, 0x54, 0x41, 0x42, 0x59,
-     0x54, 0x45, 0x10, 0x0c, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x45, 0x52, 0x54,
-     0x5a, 0x10, 0x0d, 0x12, 0x0d, 0x0a, 0x09, 0x4b, 0x49, 0x4c, 0x4f, 0x48,
-     0x45, 0x52, 0x54, 0x5a, 0x10, 0x0e, 0x12, 0x0d, 0x0a, 0x09, 0x4d, 0x45,
-     0x47, 0x41, 0x48, 0x45, 0x52, 0x54, 0x5a, 0x10, 0x0f, 0x12, 0x0d, 0x0a,
-     0x09, 0x47, 0x49, 0x47, 0x41, 0x48, 0x45, 0x52, 0x54, 0x5a, 0x10, 0x10,
-     0x12, 0x0d, 0x0a, 0x09, 0x54, 0x45, 0x52, 0x41, 0x48, 0x45, 0x52, 0x54,
-     0x5a, 0x10, 0x11, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x45, 0x54, 0x41, 0x48,
-     0x45, 0x52, 0x54, 0x5a, 0x10, 0x12, 0x12, 0x0e, 0x0a, 0x0a, 0x4e, 0x41,
-     0x4e, 0x4f, 0x53, 0x45, 0x43, 0x4f, 0x4e, 0x44, 0x10, 0x13, 0x12, 0x0f,
-     0x0a, 0x0b, 0x4d, 0x49, 0x43, 0x52, 0x4f, 0x53, 0x45, 0x43, 0x4f, 0x4e,
-     0x44, 0x10, 0x14, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x49, 0x4c, 0x4c, 0x49,
-     0x53, 0x45, 0x43, 0x4f, 0x4e, 0x44, 0x10, 0x15, 0x12, 0x0a, 0x0a, 0x06,
-     0x53, 0x45, 0x43, 0x4f, 0x4e, 0x44, 0x10, 0x16, 0x12, 0x0a, 0x0a, 0x06,
-     0x4d, 0x49, 0x4e, 0x55, 0x54, 0x45, 0x10, 0x17, 0x12, 0x08, 0x0a, 0x04,
-     0x48, 0x4f, 0x55, 0x52, 0x10, 0x18, 0x12, 0x0a, 0x0a, 0x06, 0x56, 0x45,
-     0x52, 0x54, 0x45, 0x58, 0x10, 0x19, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x49,
-     0x58, 0x45, 0x4c, 0x10, 0x1a, 0x12, 0x0c, 0x0a, 0x08, 0x54, 0x52, 0x49,
-     0x41, 0x4e, 0x47, 0x4c, 0x45, 0x10, 0x1b, 0x12, 0x0d, 0x0a, 0x09, 0x50,
-     0x52, 0x49, 0x4d, 0x49, 0x54, 0x49, 0x56, 0x45, 0x10, 0x26, 0x12, 0x0c,
-     0x0a, 0x08, 0x46, 0x52, 0x41, 0x47, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x27,
-     0x12, 0x0d, 0x0a, 0x09, 0x4d, 0x49, 0x4c, 0x4c, 0x49, 0x57, 0x41, 0x54,
-     0x54, 0x10, 0x1c, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x54, 0x54, 0x10,
-     0x1d, 0x12, 0x0c, 0x0a, 0x08, 0x4b, 0x49, 0x4c, 0x4f, 0x57, 0x41, 0x54,
-     0x54, 0x10, 0x1e, 0x12, 0x09, 0x0a, 0x05, 0x4a, 0x4f, 0x55, 0x4c, 0x45,
-     0x10, 0x1f, 0x12, 0x08, 0x0a, 0x04, 0x56, 0x4f, 0x4c, 0x54, 0x10, 0x20,
-     0x12, 0x0a, 0x0a, 0x06, 0x41, 0x4d, 0x50, 0x45, 0x52, 0x45, 0x10, 0x21,
-     0x12, 0x0b, 0x0a, 0x07, 0x43, 0x45, 0x4c, 0x53, 0x49, 0x55, 0x53, 0x10,
-     0x22, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x41, 0x48, 0x52, 0x45, 0x4e, 0x48,
-     0x45, 0x49, 0x54, 0x10, 0x23, 0x12, 0x0a, 0x0a, 0x06, 0x4b, 0x45, 0x4c,
-     0x56, 0x49, 0x4e, 0x10, 0x24, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x45, 0x52,
-     0x43, 0x45, 0x4e, 0x54, 0x10, 0x25, 0x12, 0x0f, 0x0a, 0x0b, 0x49, 0x4e,
-     0x53, 0x54, 0x52, 0x55, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x28, 0x22,
-     0xbc, 0x0a, 0x0a, 0x0a, 0x54, 0x72, 0x61, 0x63, 0x65, 0x53, 0x74, 0x61,
-     0x74, 0x73, 0x12, 0x4a, 0x0a, 0x0c, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72,
-     0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
-     0x32, 0x27, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65,
-     0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72,
-     0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x0b, 0x62, 0x75, 0x66, 0x66, 0x65,
-     0x72, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x2f, 0x0a, 0x13, 0x70, 0x72,
-     0x6f, 0x64, 0x75, 0x63, 0x65, 0x72, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x6e,
-     0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52,
-     0x12, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x72, 0x73, 0x43, 0x6f,
-     0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x70,
-     0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x72, 0x73, 0x5f, 0x73, 0x65, 0x65,
-     0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x70, 0x72, 0x6f,
-     0x64, 0x75, 0x63, 0x65, 0x72, 0x73, 0x53, 0x65, 0x65, 0x6e, 0x12, 0x36,
-     0x0a, 0x17, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63,
-     0x65, 0x73, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65,
-     0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x15, 0x64, 0x61, 0x74,
-     0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x67, 0x69,
-     0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x64, 0x61,
-     0x74, 0x61, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x5f, 0x73,
-     0x65, 0x65, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x64,
-     0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x53, 0x65,
-     0x65, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x72, 0x61, 0x63, 0x69, 0x6e,
-     0x67, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06,
-     0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x74, 0x72, 0x61, 0x63, 0x69, 0x6e,
-     0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x23, 0x0a,
-     0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65,
-     0x72, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x74, 0x6f,
-     0x74, 0x61, 0x6c, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x73, 0x12, 0x29,
-     0x0a, 0x10, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x5f, 0x64, 0x69, 0x73,
-     0x63, 0x61, 0x72, 0x64, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04,
-     0x52, 0x0f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x44, 0x69, 0x73, 0x63,
-     0x61, 0x72, 0x64, 0x65, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x70, 0x61, 0x74,
-     0x63, 0x68, 0x65, 0x73, 0x5f, 0x64, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64,
-     0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x70, 0x61,
-     0x74, 0x63, 0x68, 0x65, 0x73, 0x44, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64,
-     0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69,
-     0x64, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x0a, 0x20,
-     0x01, 0x28, 0x04, 0x52, 0x0e, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64,
-     0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x1a, 0xd4, 0x06, 0x0a, 0x0b,
-     0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12,
-     0x1f, 0x0a, 0x0b, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x73, 0x69,
-     0x7a, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x62, 0x75,
-     0x66, 0x66, 0x65, 0x72, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, 0x0d,
-     0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65,
-     0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x62, 0x79, 0x74,
-     0x65, 0x73, 0x57, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x12, 0x2b, 0x0a,
-     0x11, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x77,
-     0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04,
-     0x52, 0x10, 0x62, 0x79, 0x74, 0x65, 0x73, 0x4f, 0x76, 0x65, 0x72, 0x77,
-     0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x79,
-     0x74, 0x65, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x18, 0x0e, 0x20, 0x01,
-     0x28, 0x04, 0x52, 0x09, 0x62, 0x79, 0x74, 0x65, 0x73, 0x52, 0x65, 0x61,
-     0x64, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67,
-     0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x74,
-     0x65, 0x6e, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, 0x52, 0x13, 0x70, 0x61,
-     0x64, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x79, 0x74, 0x65, 0x73, 0x57, 0x72,
-     0x69, 0x74, 0x74, 0x65, 0x6e, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x61, 0x64,
-     0x64, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x63,
-     0x6c, 0x65, 0x61, 0x72, 0x65, 0x64, 0x18, 0x10, 0x20, 0x01, 0x28, 0x04,
-     0x52, 0x13, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x79, 0x74,
-     0x65, 0x73, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x65, 0x64, 0x12, 0x25, 0x0a,
-     0x0e, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x5f, 0x77, 0x72, 0x69, 0x74,
-     0x74, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x63,
-     0x68, 0x75, 0x6e, 0x6b, 0x73, 0x57, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e,
-     0x12, 0x29, 0x0a, 0x10, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x5f, 0x72,
-     0x65, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x18, 0x0a, 0x20, 0x01,
-     0x28, 0x04, 0x52, 0x0f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65,
-     0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x63,
-     0x68, 0x75, 0x6e, 0x6b, 0x73, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72,
-     0x69, 0x74, 0x74, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52,
-     0x11, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x4f, 0x76, 0x65, 0x72, 0x77,
-     0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x68,
-     0x75, 0x6e, 0x6b, 0x73, 0x5f, 0x64, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64,
-     0x65, 0x64, 0x18, 0x12, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x63, 0x68,
-     0x75, 0x6e, 0x6b, 0x73, 0x44, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x65,
-     0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x5f,
-     0x72, 0x65, 0x61, 0x64, 0x18, 0x11, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a,
-     0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x61, 0x64, 0x12, 0x40,
-     0x0a, 0x1d, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x5f, 0x63, 0x6f, 0x6d,
-     0x6d, 0x69, 0x74, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x6f,
-     0x66, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28,
-     0x04, 0x52, 0x19, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x43, 0x6f, 0x6d,
-     0x6d, 0x69, 0x74, 0x74, 0x65, 0x64, 0x4f, 0x75, 0x74, 0x4f, 0x66, 0x4f,
-     0x72, 0x64, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x10, 0x77, 0x72, 0x69, 0x74,
-     0x65, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74,
-     0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x77, 0x72, 0x69, 0x74,
-     0x65, 0x57, 0x72, 0x61, 0x70, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2b,
-     0x0a, 0x11, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x5f, 0x73, 0x75,
-     0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28,
-     0x04, 0x52, 0x10, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x53, 0x75,
-     0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x70,
-     0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x65,
-     0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x70, 0x61, 0x74,
-     0x63, 0x68, 0x65, 0x73, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x31,
-     0x0a, 0x14, 0x72, 0x65, 0x61, 0x64, 0x61, 0x68, 0x65, 0x61, 0x64, 0x73,
-     0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x18, 0x07,
-     0x20, 0x01, 0x28, 0x04, 0x52, 0x13, 0x72, 0x65, 0x61, 0x64, 0x61, 0x68,
-     0x65, 0x61, 0x64, 0x73, 0x53, 0x75, 0x63, 0x63, 0x65, 0x65, 0x64, 0x65,
-     0x64, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x61, 0x64, 0x61, 0x68, 0x65,
-     0x61, 0x64, 0x73, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x18, 0x08,
-     0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x72, 0x65, 0x61, 0x64, 0x61, 0x68,
-     0x65, 0x61, 0x64, 0x73, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x25,
-     0x0a, 0x0e, 0x61, 0x62, 0x69, 0x5f, 0x76, 0x69, 0x6f, 0x6c, 0x61, 0x74,
-     0x69, 0x6f, 0x6e, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d,
-     0x61, 0x62, 0x69, 0x56, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e,
-     0x73, 0x12, 0x37, 0x0a, 0x18, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x77,
-     0x72, 0x69, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74,
-     0x5f, 0x6c, 0x6f, 0x73, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x04, 0x52,
-     0x15, 0x74, 0x72, 0x61, 0x63, 0x65, 0x57, 0x72, 0x69, 0x74, 0x65, 0x72,
-     0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x4c, 0x6f, 0x73, 0x73, 0x22, 0xc8,
-     0x03, 0x0a, 0x13, 0x54, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x53, 0x65,
-     0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x4b,
-     0x0a, 0x09, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x72, 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,
-     0x54, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69,
-     0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x64,
-     0x75, 0x63, 0x65, 0x72, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63,
-     0x65, 0x72, 0x73, 0x12, 0x52, 0x0a, 0x0c, 0x64, 0x61, 0x74, 0x61, 0x5f,
-     0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
-     0x0b, 0x32, 0x2f, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63,
-     0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74,
-     0x61, 0x74, 0x65, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72,
-     0x63, 0x65, 0x52, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72,
-     0x63, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x75, 0x6d, 0x5f, 0x73,
-     0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
-     0x05, 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f,
-     0x6e, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x65,
-     0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74,
-     0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x6e, 0x75,
-     0x6d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x61,
-     0x72, 0x74, 0x65, 0x64, 0x1a, 0x40, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x64,
-     0x75, 0x63, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
-     0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04,
-     0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
-     0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18,
-     0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x75, 0x69, 0x64, 0x1a, 0x79,
-     0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65,
-     0x12, 0x4a, 0x0a, 0x0d, 0x64, 0x73, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72,
-     0x69, 0x70, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
-     0x25, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f,
-     0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
-     0x6f, 0x72, 0x52, 0x0c, 0x64, 0x73, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69,
-     0x70, 0x74, 0x6f, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x64,
-     0x75, 0x63, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
-     0x05, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x72, 0x49,
-     0x64, 0x22, 0x49, 0x0a, 0x14, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x45, 0x76,
+     0x6e, 0x69, 0x74, 0x52, 0x10, 0x64, 0x65, 0x6e, 0x6f, 0x6d, 0x69, 0x6e,
+     0x61, 0x74, 0x6f, 0x72, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x12, 0x2a, 0x0a,
+     0x11, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x5f, 0x62, 0x79, 0x5f, 0x64,
+     0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08,
+     0x52, 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x42, 0x79, 0x44, 0x65,
+     0x66, 0x61, 0x75, 0x6c, 0x74, 0x42, 0x0c, 0x0a, 0x0a, 0x70, 0x65, 0x61,
+     0x6b, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4a, 0x04, 0x08, 0x04, 0x10,
+     0x05, 0x1a, 0xaa, 0x01, 0x0a, 0x0f, 0x47, 0x70, 0x75, 0x43, 0x6f, 0x75,
+     0x6e, 0x74, 0x65, 0x72, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x19, 0x0a,
+     0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20,
+     0x01, 0x28, 0x0d, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64,
+     0x12, 0x25, 0x0a, 0x0e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x61,
+     0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d,
+     0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x61, 0x70, 0x61, 0x63,
+     0x69, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
+     0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
+     0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
+     0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65,
+     0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a,
+     0x0b, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73,
+     0x18, 0x05, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x6f, 0x75, 0x6e,
+     0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0xac, 0x04, 0x0a, 0x0b, 0x4d,
+     0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x12, 0x08,
+     0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03,
+     0x42, 0x49, 0x54, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x49, 0x4c,
+     0x4f, 0x42, 0x49, 0x54, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x4d, 0x45,
+     0x47, 0x41, 0x42, 0x49, 0x54, 0x10, 0x03, 0x12, 0x0b, 0x0a, 0x07, 0x47,
+     0x49, 0x47, 0x41, 0x42, 0x49, 0x54, 0x10, 0x04, 0x12, 0x0b, 0x0a, 0x07,
+     0x54, 0x45, 0x52, 0x41, 0x42, 0x49, 0x54, 0x10, 0x05, 0x12, 0x0b, 0x0a,
+     0x07, 0x50, 0x45, 0x54, 0x41, 0x42, 0x49, 0x54, 0x10, 0x06, 0x12, 0x08,
+     0x0a, 0x04, 0x42, 0x59, 0x54, 0x45, 0x10, 0x07, 0x12, 0x0c, 0x0a, 0x08,
+     0x4b, 0x49, 0x4c, 0x4f, 0x42, 0x59, 0x54, 0x45, 0x10, 0x08, 0x12, 0x0c,
+     0x0a, 0x08, 0x4d, 0x45, 0x47, 0x41, 0x42, 0x59, 0x54, 0x45, 0x10, 0x09,
+     0x12, 0x0c, 0x0a, 0x08, 0x47, 0x49, 0x47, 0x41, 0x42, 0x59, 0x54, 0x45,
+     0x10, 0x0a, 0x12, 0x0c, 0x0a, 0x08, 0x54, 0x45, 0x52, 0x41, 0x42, 0x59,
+     0x54, 0x45, 0x10, 0x0b, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x45, 0x54, 0x41,
+     0x42, 0x59, 0x54, 0x45, 0x10, 0x0c, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x45,
+     0x52, 0x54, 0x5a, 0x10, 0x0d, 0x12, 0x0d, 0x0a, 0x09, 0x4b, 0x49, 0x4c,
+     0x4f, 0x48, 0x45, 0x52, 0x54, 0x5a, 0x10, 0x0e, 0x12, 0x0d, 0x0a, 0x09,
+     0x4d, 0x45, 0x47, 0x41, 0x48, 0x45, 0x52, 0x54, 0x5a, 0x10, 0x0f, 0x12,
+     0x0d, 0x0a, 0x09, 0x47, 0x49, 0x47, 0x41, 0x48, 0x45, 0x52, 0x54, 0x5a,
+     0x10, 0x10, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x45, 0x52, 0x41, 0x48, 0x45,
+     0x52, 0x54, 0x5a, 0x10, 0x11, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x45, 0x54,
+     0x41, 0x48, 0x45, 0x52, 0x54, 0x5a, 0x10, 0x12, 0x12, 0x0e, 0x0a, 0x0a,
+     0x4e, 0x41, 0x4e, 0x4f, 0x53, 0x45, 0x43, 0x4f, 0x4e, 0x44, 0x10, 0x13,
+     0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x49, 0x43, 0x52, 0x4f, 0x53, 0x45, 0x43,
+     0x4f, 0x4e, 0x44, 0x10, 0x14, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x49, 0x4c,
+     0x4c, 0x49, 0x53, 0x45, 0x43, 0x4f, 0x4e, 0x44, 0x10, 0x15, 0x12, 0x0a,
+     0x0a, 0x06, 0x53, 0x45, 0x43, 0x4f, 0x4e, 0x44, 0x10, 0x16, 0x12, 0x0a,
+     0x0a, 0x06, 0x4d, 0x49, 0x4e, 0x55, 0x54, 0x45, 0x10, 0x17, 0x12, 0x08,
+     0x0a, 0x04, 0x48, 0x4f, 0x55, 0x52, 0x10, 0x18, 0x12, 0x0a, 0x0a, 0x06,
+     0x56, 0x45, 0x52, 0x54, 0x45, 0x58, 0x10, 0x19, 0x12, 0x09, 0x0a, 0x05,
+     0x50, 0x49, 0x58, 0x45, 0x4c, 0x10, 0x1a, 0x12, 0x0c, 0x0a, 0x08, 0x54,
+     0x52, 0x49, 0x41, 0x4e, 0x47, 0x4c, 0x45, 0x10, 0x1b, 0x12, 0x0d, 0x0a,
+     0x09, 0x50, 0x52, 0x49, 0x4d, 0x49, 0x54, 0x49, 0x56, 0x45, 0x10, 0x26,
+     0x12, 0x0c, 0x0a, 0x08, 0x46, 0x52, 0x41, 0x47, 0x4d, 0x45, 0x4e, 0x54,
+     0x10, 0x27, 0x12, 0x0d, 0x0a, 0x09, 0x4d, 0x49, 0x4c, 0x4c, 0x49, 0x57,
+     0x41, 0x54, 0x54, 0x10, 0x1c, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x54,
+     0x54, 0x10, 0x1d, 0x12, 0x0c, 0x0a, 0x08, 0x4b, 0x49, 0x4c, 0x4f, 0x57,
+     0x41, 0x54, 0x54, 0x10, 0x1e, 0x12, 0x09, 0x0a, 0x05, 0x4a, 0x4f, 0x55,
+     0x4c, 0x45, 0x10, 0x1f, 0x12, 0x08, 0x0a, 0x04, 0x56, 0x4f, 0x4c, 0x54,
+     0x10, 0x20, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x4d, 0x50, 0x45, 0x52, 0x45,
+     0x10, 0x21, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x45, 0x4c, 0x53, 0x49, 0x55,
+     0x53, 0x10, 0x22, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x41, 0x48, 0x52, 0x45,
+     0x4e, 0x48, 0x45, 0x49, 0x54, 0x10, 0x23, 0x12, 0x0a, 0x0a, 0x06, 0x4b,
+     0x45, 0x4c, 0x56, 0x49, 0x4e, 0x10, 0x24, 0x12, 0x0b, 0x0a, 0x07, 0x50,
+     0x45, 0x52, 0x43, 0x45, 0x4e, 0x54, 0x10, 0x25, 0x12, 0x0f, 0x0a, 0x0b,
+     0x49, 0x4e, 0x53, 0x54, 0x52, 0x55, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10,
+     0x28, 0x22, 0x5e, 0x0a, 0x12, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x45, 0x76,
+     0x65, 0x6e, 0x74, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12,
+     0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+     0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64,
+     0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02,
+     0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
+     0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67,
+     0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67,
+     0x73, 0x22, 0x6e, 0x0a, 0x14, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x45, 0x76,
      0x65, 0x6e, 0x74, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f,
-     0x72, 0x12, 0x31, 0x0a, 0x14, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62,
+     0x72, 0x12, 0x56, 0x0a, 0x14, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62,
      0x6c, 0x65, 0x5f, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65,
-     0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x61, 0x76, 0x61,
-     0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f,
-     0x72, 0x69, 0x65, 0x73, 0x22, 0xb1, 0x01, 0x0a, 0x10, 0x41, 0x6e, 0x64,
-     0x72, 0x6f, 0x69, 0x64, 0x4c, 0x6f, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69,
-     0x67, 0x12, 0x36, 0x0a, 0x07, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x73,
-     0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x70, 0x65, 0x72,
+     0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, 0x65,
+     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74,
+     0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x52, 0x13, 0x61, 0x76,
+     0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67,
+     0x6f, 0x72, 0x69, 0x65, 0x73, 0x22, 0x93, 0x03, 0x0a, 0x14, 0x44, 0x61,
+     0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x73, 0x63,
+     0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
+     0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
+     0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x13, 0x77, 0x69, 0x6c, 0x6c, 0x5f, 0x6e,
+     0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x6f,
+     0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x77, 0x69, 0x6c,
+     0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4f, 0x6e, 0x53, 0x74, 0x6f,
+     0x70, 0x12, 0x2f, 0x0a, 0x14, 0x77, 0x69, 0x6c, 0x6c, 0x5f, 0x6e, 0x6f,
+     0x74, 0x69, 0x66, 0x79, 0x5f, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x72,
+     0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x77, 0x69, 0x6c,
+     0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4f, 0x6e, 0x53, 0x74, 0x61,
+     0x72, 0x74, 0x12, 0x45, 0x0a, 0x1f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65,
+     0x73, 0x5f, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61,
+     0x6c, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x6c, 0x65, 0x61,
+     0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x68, 0x61, 0x6e,
+     0x64, 0x6c, 0x65, 0x73, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e,
+     0x74, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x65, 0x61,
+     0x72, 0x12, 0x5f, 0x0a, 0x16, 0x67, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x75,
+     0x6e, 0x74, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+     0x74, 0x6f, 0x72, 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, 0x47, 0x70, 0x75, 0x43, 0x6f, 0x75, 0x6e, 0x74,
+     0x65, 0x72, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72,
+     0x42, 0x02, 0x28, 0x01, 0x52, 0x14, 0x67, 0x70, 0x75, 0x43, 0x6f, 0x75,
+     0x6e, 0x74, 0x65, 0x72, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+     0x6f, 0x72, 0x12, 0x5f, 0x0a, 0x16, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f,
+     0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
+     0x70, 0x74, 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25,
+     0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x45, 0x76,
+     0x65, 0x6e, 0x74, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f,
+     0x72, 0x42, 0x02, 0x28, 0x01, 0x52, 0x14, 0x74, 0x72, 0x61, 0x63, 0x6b,
+     0x45, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+     0x74, 0x6f, 0x72, 0x22, 0xc8, 0x03, 0x0a, 0x13, 0x54, 0x72, 0x61, 0x63,
+     0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74,
+     0x61, 0x74, 0x65, 0x12, 0x4b, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x64, 0x75,
+     0x63, 0x65, 0x72, 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, 0x54, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67,
+     0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65,
+     0x2e, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x72, 0x52, 0x09, 0x70,
+     0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x72, 0x73, 0x12, 0x52, 0x0a, 0x0c,
+     0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
+     0x18, 0x02, 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, 0x6f, 0x67, 0x49,
-     0x64, 0x52, 0x06, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x73, 0x12, 0x3e, 0x0a,
-     0x08, 0x6d, 0x69, 0x6e, 0x5f, 0x70, 0x72, 0x69, 0x6f, 0x18, 0x03, 0x20,
-     0x01, 0x28, 0x0e, 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, 0x4c, 0x6f, 0x67, 0x50, 0x72, 0x69, 0x6f,
-     0x72, 0x69, 0x74, 0x79, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x50, 0x72, 0x69,
-     0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f,
-     0x74, 0x61, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a,
-     0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x73, 0x4a, 0x04,
-     0x08, 0x02, 0x10, 0x03, 0x22, 0x6d, 0x0a, 0x0c, 0x43, 0x68, 0x72, 0x6f,
+     0x2e, 0x54, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76,
+     0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x44, 0x61, 0x74,
+     0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0b, 0x64, 0x61, 0x74,
+     0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c,
+     0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73,
+     0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x53,
+     0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x6e,
+     0x75, 0x6d, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5f,
+     0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28,
+     0x05, 0x52, 0x12, 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f,
+     0x6e, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x1a, 0x40, 0x0a,
+     0x08, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x72, 0x12, 0x0e, 0x0a,
+     0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69,
+     0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
+     0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a,
+     0x03, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03,
+     0x75, 0x69, 0x64, 0x1a, 0x79, 0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x53,
+     0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x64, 0x73, 0x5f,
+     0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x18, 0x01,
+     0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
+     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x44,
+     0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x73,
+     0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x0c, 0x64, 0x73, 0x44,
+     0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x1f, 0x0a,
+     0x0b, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x72, 0x5f, 0x69, 0x64,
+     0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x64,
+     0x75, 0x63, 0x65, 0x72, 0x49, 0x64, 0x22, 0xb1, 0x01, 0x0a, 0x10, 0x41,
+     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4c, 0x6f, 0x67, 0x43, 0x6f, 0x6e,
+     0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x07, 0x6c, 0x6f, 0x67, 0x5f, 0x69,
+     0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x1d, 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, 0x6f,
+     0x67, 0x49, 0x64, 0x52, 0x06, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x73, 0x12,
+     0x3e, 0x0a, 0x08, 0x6d, 0x69, 0x6e, 0x5f, 0x70, 0x72, 0x69, 0x6f, 0x18,
+     0x03, 0x20, 0x01, 0x28, 0x0e, 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, 0x4c, 0x6f, 0x67, 0x50, 0x72,
+     0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x50,
+     0x72, 0x69, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x69, 0x6c, 0x74, 0x65,
+     0x72, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09,
+     0x52, 0x0a, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x73,
+     0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0x44, 0x0a, 0x12, 0x50, 0x61,
+     0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f,
+     0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x61, 0x63, 0x6b,
+     0x61, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x66, 0x69, 0x6c,
+     0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x70,
+     0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x46, 0x69,
+     0x6c, 0x74, 0x65, 0x72, 0x22, 0x6d, 0x0a, 0x0c, 0x43, 0x68, 0x72, 0x6f,
      0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x21, 0x0a, 0x0c,
      0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
      0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x72, 0x61, 0x63,
@@ -382,7 +287,442 @@
      0x72, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
      0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x70, 0x72, 0x69, 0x76,
      0x61, 0x63, 0x79, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67,
-     0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0xaf, 0x0b, 0x0a, 0x10,
+     0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0xd6, 0x02, 0x0a, 0x0c,
+     0x46, 0x74, 0x72, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+     0x12, 0x23, 0x0a, 0x0d, 0x66, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x65,
+     0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52,
+     0x0c, 0x66, 0x74, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74,
+     0x73, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f,
+     0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x18, 0x02,
+     0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x61, 0x74, 0x72, 0x61, 0x63, 0x65,
+     0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x12, 0x1f,
+     0x0a, 0x0b, 0x61, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x61, 0x70, 0x70,
+     0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x74, 0x72,
+     0x61, 0x63, 0x65, 0x41, 0x70, 0x70, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x62,
+     0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6b,
+     0x62, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x62, 0x75, 0x66,
+     0x66, 0x65, 0x72, 0x53, 0x69, 0x7a, 0x65, 0x4b, 0x62, 0x12, 0x26, 0x0a,
+     0x0f, 0x64, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f,
+     0x64, 0x5f, 0x6d, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d,
+     0x64, 0x72, 0x61, 0x69, 0x6e, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x4d,
+     0x73, 0x12, 0x55, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74,
+     0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b,
+     0x32, 0x30, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x46, 0x74, 0x72, 0x61, 0x63,
+     0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x6f, 0x6d, 0x70,
+     0x61, 0x63, 0x74, 0x53, 0x63, 0x68, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x66,
+     0x69, 0x67, 0x52, 0x0c, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x53,
+     0x63, 0x68, 0x65, 0x64, 0x1a, 0x2e, 0x0a, 0x12, 0x43, 0x6f, 0x6d, 0x70,
+     0x61, 0x63, 0x74, 0x53, 0x63, 0x68, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x66,
+     0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65,
+     0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61,
+     0x62, 0x6c, 0x65, 0x64, 0x22, 0xb8, 0x01, 0x0a, 0x10, 0x47, 0x70, 0x75,
+     0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69,
+     0x67, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72,
+     0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x6e, 0x73, 0x18, 0x01,
+     0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65,
+     0x72, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x4e, 0x73, 0x12, 0x1f, 0x0a,
+     0x0b, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73,
+     0x18, 0x02, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x6f, 0x75, 0x6e,
+     0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x33, 0x0a, 0x15, 0x69, 0x6e,
+     0x73, 0x74, 0x72, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x5f, 0x73,
+     0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28,
+     0x08, 0x52, 0x14, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x6d, 0x65, 0x6e,
+     0x74, 0x65, 0x64, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x12,
+     0x22, 0x0a, 0x0d, 0x66, 0x69, 0x78, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x63,
+     0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b,
+     0x66, 0x69, 0x78, 0x47, 0x70, 0x75, 0x43, 0x6c, 0x6f, 0x63, 0x6b, 0x22,
+     0x8a, 0x01, 0x0a, 0x12, 0x56, 0x75, 0x6c, 0x6b, 0x61, 0x6e, 0x4d, 0x65,
+     0x6d, 0x6f, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39,
+     0x0a, 0x19, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x64, 0x72, 0x69, 0x76,
+     0x65, 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73,
+     0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x74,
+     0x72, 0x61, 0x63, 0x6b, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x4d, 0x65,
+     0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x39, 0x0a,
+     0x19, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63,
+     0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, 0x61,
+     0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x74, 0x72,
+     0x61, 0x63, 0x6b, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x6d,
+     0x6f, 0x72, 0x79, 0x55, 0x73, 0x61, 0x67, 0x65, 0x22, 0x95, 0x03, 0x0a,
+     0x0f, 0x49, 0x6e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x6f,
+     0x6e, 0x66, 0x69, 0x67, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x63, 0x61, 0x6e,
+     0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x6d, 0x73,
+     0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x73, 0x63, 0x61, 0x6e,
+     0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x73, 0x12, 0x22,
+     0x0a, 0x0d, 0x73, 0x63, 0x61, 0x6e, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79,
+     0x5f, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73,
+     0x63, 0x61, 0x6e, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x73, 0x12, 0x26,
+     0x0a, 0x0f, 0x73, 0x63, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68,
+     0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52,
+     0x0d, 0x73, 0x63, 0x61, 0x6e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x53, 0x69,
+     0x7a, 0x65, 0x12, 0x1e, 0x0a, 0x0b, 0x64, 0x6f, 0x5f, 0x6e, 0x6f, 0x74,
+     0x5f, 0x73, 0x63, 0x61, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52,
+     0x09, 0x64, 0x6f, 0x4e, 0x6f, 0x74, 0x53, 0x63, 0x61, 0x6e, 0x12, 0x2a,
+     0x0a, 0x11, 0x73, 0x63, 0x61, 0x6e, 0x5f, 0x6d, 0x6f, 0x75, 0x6e, 0x74,
+     0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28,
+     0x09, 0x52, 0x0f, 0x73, 0x63, 0x61, 0x6e, 0x4d, 0x6f, 0x75, 0x6e, 0x74,
+     0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x67, 0x0a, 0x13, 0x6d, 0x6f,
+     0x75, 0x6e, 0x74, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x6d, 0x61,
+     0x70, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32,
+     0x37, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x49, 0x6e, 0x6f, 0x64, 0x65, 0x46,
+     0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x4d, 0x6f,
+     0x75, 0x6e, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x70,
+     0x69, 0x6e, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x6d, 0x6f,
+     0x75, 0x6e, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x70,
+     0x69, 0x6e, 0x67, 0x1a, 0x57, 0x0a, 0x16, 0x4d, 0x6f, 0x75, 0x6e, 0x74,
+     0x50, 0x6f, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67,
+     0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x6f, 0x75,
+     0x6e, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
+     0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x70, 0x6f, 0x69, 0x6e,
+     0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x63, 0x61, 0x6e, 0x5f, 0x72, 0x6f,
+     0x6f, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x73,
+     0x63, 0x61, 0x6e, 0x52, 0x6f, 0x6f, 0x74, 0x73, 0x22, 0x81, 0x03, 0x0a,
+     0x12, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x6f, 0x77, 0x65,
+     0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x26, 0x0a, 0x0f, 0x62,
+     0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x5f, 0x70, 0x6f, 0x6c, 0x6c, 0x5f,
+     0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x62, 0x61,
+     0x74, 0x74, 0x65, 0x72, 0x79, 0x50, 0x6f, 0x6c, 0x6c, 0x4d, 0x73, 0x12,
+     0x5e, 0x0a, 0x10, 0x62, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x5f, 0x63,
+     0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
+     0x0e, 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, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66,
+     0x69, 0x67, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x43, 0x6f,
+     0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x52, 0x0f, 0x62, 0x61, 0x74, 0x74,
+     0x65, 0x72, 0x79, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12,
+     0x2e, 0x0a, 0x13, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x5f, 0x70,
+     0x6f, 0x77, 0x65, 0x72, 0x5f, 0x72, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x03,
+     0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63,
+     0x74, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x61, 0x69, 0x6c, 0x73, 0x22,
+     0xb2, 0x01, 0x0a, 0x0f, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x43,
+     0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1f, 0x0a, 0x1b, 0x42,
+     0x41, 0x54, 0x54, 0x45, 0x52, 0x59, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54,
+     0x45, 0x52, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49,
+     0x45, 0x44, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x42, 0x41, 0x54, 0x54,
+     0x45, 0x52, 0x59, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x45, 0x52, 0x5f,
+     0x43, 0x48, 0x41, 0x52, 0x47, 0x45, 0x10, 0x01, 0x12, 0x24, 0x0a, 0x20,
+     0x42, 0x41, 0x54, 0x54, 0x45, 0x52, 0x59, 0x5f, 0x43, 0x4f, 0x55, 0x4e,
+     0x54, 0x45, 0x52, 0x5f, 0x43, 0x41, 0x50, 0x41, 0x43, 0x49, 0x54, 0x59,
+     0x5f, 0x50, 0x45, 0x52, 0x43, 0x45, 0x4e, 0x54, 0x10, 0x02, 0x12, 0x1b,
+     0x0a, 0x17, 0x42, 0x41, 0x54, 0x54, 0x45, 0x52, 0x59, 0x5f, 0x43, 0x4f,
+     0x55, 0x4e, 0x54, 0x45, 0x52, 0x5f, 0x43, 0x55, 0x52, 0x52, 0x45, 0x4e,
+     0x54, 0x10, 0x03, 0x12, 0x1f, 0x0a, 0x1b, 0x42, 0x41, 0x54, 0x54, 0x45,
+     0x52, 0x59, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x45, 0x52, 0x5f, 0x43,
+     0x55, 0x52, 0x52, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x56, 0x47, 0x10, 0x04,
+     0x22, 0x83, 0x04, 0x0a, 0x12, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
+     0x53, 0x74, 0x61, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
+     0x42, 0x0a, 0x06, 0x71, 0x75, 0x69, 0x72, 0x6b, 0x73, 0x18, 0x01, 0x20,
+     0x03, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x50, 0x72,
+     0x6f, 0x63, 0x65, 0x73, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x43, 0x6f,
+     0x6e, 0x66, 0x69, 0x67, 0x2e, 0x51, 0x75, 0x69, 0x72, 0x6b, 0x73, 0x52,
+     0x06, 0x71, 0x75, 0x69, 0x72, 0x6b, 0x73, 0x12, 0x3c, 0x0a, 0x1b, 0x73,
+     0x63, 0x61, 0x6e, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x63,
+     0x65, 0x73, 0x73, 0x65, 0x73, 0x5f, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61,
+     0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x73, 0x63,
+     0x61, 0x6e, 0x41, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
+     0x65, 0x73, 0x4f, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2e, 0x0a,
+     0x13, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x74, 0x68, 0x72, 0x65,
+     0x61, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01,
+     0x28, 0x08, 0x52, 0x11, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x54, 0x68,
+     0x72, 0x65, 0x61, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x2b, 0x0a,
+     0x12, 0x70, 0x72, 0x6f, 0x63, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x5f,
+     0x70, 0x6f, 0x6c, 0x6c, 0x5f, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28,
+     0x0d, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x63, 0x53, 0x74, 0x61, 0x74, 0x73,
+     0x50, 0x6f, 0x6c, 0x6c, 0x4d, 0x73, 0x12, 0x34, 0x0a, 0x17, 0x70, 0x72,
+     0x6f, 0x63, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x5f, 0x63, 0x61, 0x63,
+     0x68, 0x65, 0x5f, 0x74, 0x74, 0x6c, 0x5f, 0x6d, 0x73, 0x18, 0x06, 0x20,
+     0x01, 0x28, 0x0d, 0x52, 0x13, 0x70, 0x72, 0x6f, 0x63, 0x53, 0x74, 0x61,
+     0x74, 0x73, 0x43, 0x61, 0x63, 0x68, 0x65, 0x54, 0x74, 0x6c, 0x4d, 0x73,
+     0x12, 0x3c, 0x0a, 0x1b, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x74,
+     0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x69,
+     0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28,
+     0x08, 0x52, 0x17, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x54, 0x68, 0x72,
+     0x65, 0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x53, 0x74, 0x61,
+     0x74, 0x65, 0x12, 0x43, 0x0a, 0x1f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64,
+     0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x61,
+     0x74, 0x65, 0x5f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x73, 0x69, 0x7a,
+     0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x1a, 0x74, 0x68, 0x72,
+     0x65, 0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x53, 0x74, 0x61,
+     0x74, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x22,
+     0x55, 0x0a, 0x06, 0x51, 0x75, 0x69, 0x72, 0x6b, 0x73, 0x12, 0x16, 0x0a,
+     0x12, 0x51, 0x55, 0x49, 0x52, 0x4b, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50,
+     0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a,
+     0x14, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x49, 0x4e, 0x49,
+     0x54, 0x49, 0x41, 0x4c, 0x5f, 0x44, 0x55, 0x4d, 0x50, 0x10, 0x01, 0x1a,
+     0x02, 0x08, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x44, 0x49, 0x53, 0x41, 0x42,
+     0x4c, 0x45, 0x5f, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x4d, 0x41, 0x4e, 0x44,
+     0x10, 0x02, 0x22, 0xfb, 0x06, 0x0a, 0x0f, 0x48, 0x65, 0x61, 0x70, 0x70,
+     0x72, 0x6f, 0x66, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36,
+     0x0a, 0x17, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x5f, 0x69,
+     0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65,
+     0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x73, 0x61, 0x6d,
+     0x70, 0x6c, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61,
+     0x6c, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x72,
+     0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6d, 0x64, 0x6c, 0x69, 0x6e,
+     0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x72, 0x6f,
+     0x63, 0x65, 0x73, 0x73, 0x43, 0x6d, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12,
+     0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x04, 0x20, 0x03, 0x28, 0x04,
+     0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c,
+     0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x12,
+     0x35, 0x0a, 0x17, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x6e, 0x6f, 0x6e, 0x79,
+     0x6d, 0x6f, 0x75, 0x73, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f,
+     0x6b, 0x62, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, 0x6d, 0x69,
+     0x6e, 0x41, 0x6e, 0x6f, 0x6e, 0x79, 0x6d, 0x6f, 0x75, 0x73, 0x4d, 0x65,
+     0x6d, 0x6f, 0x72, 0x79, 0x4b, 0x62, 0x12, 0x35, 0x0a, 0x17, 0x6d, 0x61,
+     0x78, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x66, 0x64, 0x5f,
+     0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6b, 0x62, 0x18, 0x10, 0x20,
+     0x01, 0x28, 0x0d, 0x52, 0x14, 0x6d, 0x61, 0x78, 0x48, 0x65, 0x61, 0x70,
+     0x70, 0x72, 0x6f, 0x66, 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4b,
+     0x62, 0x12, 0x33, 0x0a, 0x16, 0x6d, 0x61, 0x78, 0x5f, 0x68, 0x65, 0x61,
+     0x70, 0x70, 0x72, 0x6f, 0x66, 0x64, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x73,
+     0x65, 0x63, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x04, 0x52, 0x13, 0x6d,
+     0x61, 0x78, 0x48, 0x65, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x66, 0x64, 0x43,
+     0x70, 0x75, 0x53, 0x65, 0x63, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x6b,
+     0x69, 0x70, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x5f, 0x70, 0x72,
+     0x65, 0x66, 0x69, 0x78, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10,
+     0x73, 0x6b, 0x69, 0x70, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x50, 0x72,
+     0x65, 0x66, 0x69, 0x78, 0x12, 0x6b, 0x0a, 0x16, 0x63, 0x6f, 0x6e, 0x74,
+     0x69, 0x6e, 0x75, 0x6f, 0x75, 0x73, 0x5f, 0x64, 0x75, 0x6d, 0x70, 0x5f,
+     0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b,
+     0x32, 0x35, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x48, 0x65, 0x61, 0x70, 0x70,
+     0x72, 0x6f, 0x66, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43,
+     0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x6f, 0x75, 0x73, 0x44, 0x75, 0x6d,
+     0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x14, 0x63, 0x6f, 0x6e,
+     0x74, 0x69, 0x6e, 0x75, 0x6f, 0x75, 0x73, 0x44, 0x75, 0x6d, 0x70, 0x43,
+     0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x68, 0x6d,
+     0x65, 0x6d, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65,
+     0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x73, 0x68, 0x6d,
+     0x65, 0x6d, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12,
+     0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6c, 0x69,
+     0x65, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x62,
+     0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x35,
+     0x0a, 0x17, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6c, 0x69, 0x65,
+     0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x75,
+     0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, 0x62, 0x6c, 0x6f,
+     0x63, 0x6b, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65,
+     0x6f, 0x75, 0x74, 0x55, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x6f, 0x5f,
+     0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28,
+     0x08, 0x52, 0x09, 0x6e, 0x6f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70,
+     0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x6f, 0x5f, 0x72, 0x75, 0x6e, 0x6e, 0x69,
+     0x6e, 0x67, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6e, 0x6f,
+     0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x29, 0x0a, 0x10, 0x69,
+     0x64, 0x6c, 0x65, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69,
+     0x6f, 0x6e, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x69,
+     0x64, 0x6c, 0x65, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f,
+     0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x61,
+     0x74, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52,
+     0x09, 0x64, 0x75, 0x6d, 0x70, 0x41, 0x74, 0x4d, 0x61, 0x78, 0x12, 0x32,
+     0x0a, 0x15, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x66, 0x6f,
+     0x72, 0x6b, 0x5f, 0x74, 0x65, 0x61, 0x72, 0x64, 0x6f, 0x77, 0x6e, 0x18,
+     0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62,
+     0x6c, 0x65, 0x46, 0x6f, 0x72, 0x6b, 0x54, 0x65, 0x61, 0x72, 0x64, 0x6f,
+     0x77, 0x6e, 0x1a, 0x64, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e,
+     0x75, 0x6f, 0x75, 0x73, 0x44, 0x75, 0x6d, 0x70, 0x43, 0x6f, 0x6e, 0x66,
+     0x69, 0x67, 0x12, 0x22, 0x0a, 0x0d, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x70,
+     0x68, 0x61, 0x73, 0x65, 0x5f, 0x6d, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28,
+     0x0d, 0x52, 0x0b, 0x64, 0x75, 0x6d, 0x70, 0x50, 0x68, 0x61, 0x73, 0x65,
+     0x4d, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x69,
+     0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x6d, 0x73, 0x18, 0x06,
+     0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x64, 0x75, 0x6d, 0x70, 0x49, 0x6e,
+     0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x73, 0x22, 0xf5, 0x02, 0x0a,
+     0x0f, 0x4a, 0x61, 0x76, 0x61, 0x48, 0x70, 0x72, 0x6f, 0x66, 0x43, 0x6f,
+     0x6e, 0x66, 0x69, 0x67, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x63,
+     0x65, 0x73, 0x73, 0x5f, 0x63, 0x6d, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18,
+     0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x63, 0x65,
+     0x73, 0x73, 0x43, 0x6d, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x10, 0x0a,
+     0x03, 0x70, 0x69, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x03,
+     0x70, 0x69, 0x64, 0x12, 0x6b, 0x0a, 0x16, 0x63, 0x6f, 0x6e, 0x74, 0x69,
+     0x6e, 0x75, 0x6f, 0x75, 0x73, 0x5f, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x63,
+     0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32,
+     0x35, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4a, 0x61, 0x76, 0x61, 0x48, 0x70,
+     0x72, 0x6f, 0x66, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x6f,
+     0x6e, 0x74, 0x69, 0x6e, 0x75, 0x6f, 0x75, 0x73, 0x44, 0x75, 0x6d, 0x70,
+     0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x14, 0x63, 0x6f, 0x6e, 0x74,
+     0x69, 0x6e, 0x75, 0x6f, 0x75, 0x73, 0x44, 0x75, 0x6d, 0x70, 0x43, 0x6f,
+     0x6e, 0x66, 0x69, 0x67, 0x12, 0x35, 0x0a, 0x17, 0x6d, 0x69, 0x6e, 0x5f,
+     0x61, 0x6e, 0x6f, 0x6e, 0x79, 0x6d, 0x6f, 0x75, 0x73, 0x5f, 0x6d, 0x65,
+     0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6b, 0x62, 0x18, 0x04, 0x20, 0x01, 0x28,
+     0x0d, 0x52, 0x14, 0x6d, 0x69, 0x6e, 0x41, 0x6e, 0x6f, 0x6e, 0x79, 0x6d,
+     0x6f, 0x75, 0x73, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4b, 0x62, 0x12,
+     0x1d, 0x0a, 0x0a, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x73, 0x6d, 0x61, 0x70,
+     0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x64, 0x75, 0x6d,
+     0x70, 0x53, 0x6d, 0x61, 0x70, 0x73, 0x1a, 0x64, 0x0a, 0x14, 0x43, 0x6f,
+     0x6e, 0x74, 0x69, 0x6e, 0x75, 0x6f, 0x75, 0x73, 0x44, 0x75, 0x6d, 0x70,
+     0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x22, 0x0a, 0x0d, 0x64, 0x75,
+     0x6d, 0x70, 0x5f, 0x70, 0x68, 0x61, 0x73, 0x65, 0x5f, 0x6d, 0x73, 0x18,
+     0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x64, 0x75, 0x6d, 0x70, 0x50,
+     0x68, 0x61, 0x73, 0x65, 0x4d, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x64, 0x75,
+     0x6d, 0x70, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f,
+     0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x64, 0x75,
+     0x6d, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x73,
+     0x22, 0xd4, 0x03, 0x0a, 0x0f, 0x50, 0x65, 0x72, 0x66, 0x45, 0x76, 0x65,
+     0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08,
+     0x61, 0x6c, 0x6c, 0x5f, 0x63, 0x70, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01,
+     0x28, 0x08, 0x52, 0x07, 0x61, 0x6c, 0x6c, 0x43, 0x70, 0x75, 0x73, 0x12,
+     0x2d, 0x0a, 0x12, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x5f,
+     0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20,
+     0x01, 0x28, 0x0d, 0x52, 0x11, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e,
+     0x67, 0x46, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x3a,
+     0x0a, 0x1a, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65,
+     0x72, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f,
+     0x64, 0x5f, 0x6d, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x16,
+     0x72, 0x69, 0x6e, 0x67, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65,
+     0x61, 0x64, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x4d, 0x73, 0x12, 0x2a,
+     0x0a, 0x11, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65,
+     0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
+     0x0d, 0x52, 0x0f, 0x72, 0x69, 0x6e, 0x67, 0x42, 0x75, 0x66, 0x66, 0x65,
+     0x72, 0x50, 0x61, 0x67, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x61,
+     0x72, 0x67, 0x65, 0x74, 0x5f, 0x70, 0x69, 0x64, 0x18, 0x04, 0x20, 0x03,
+     0x28, 0x05, 0x52, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x69,
+     0x64, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f,
+     0x63, 0x6d, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28,
+     0x09, 0x52, 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x6d, 0x64,
+     0x6c, 0x69, 0x6e, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x63, 0x6c,
+     0x75, 0x64, 0x65, 0x5f, 0x70, 0x69, 0x64, 0x18, 0x06, 0x20, 0x03, 0x28,
+     0x05, 0x52, 0x0a, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x50, 0x69,
+     0x64, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65,
+     0x5f, 0x63, 0x6d, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x07, 0x20, 0x03,
+     0x28, 0x09, 0x52, 0x0e, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x43,
+     0x6d, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x3f, 0x0a, 0x1c, 0x72, 0x65,
+     0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+     0x74, 0x6f, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f,
+     0x6d, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x19, 0x72, 0x65,
+     0x6d, 0x6f, 0x74, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+     0x6f, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x4d, 0x73, 0x12,
+     0x3e, 0x0a, 0x1c, 0x75, 0x6e, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x73, 0x74,
+     0x61, 0x74, 0x65, 0x5f, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x5f, 0x70, 0x65,
+     0x72, 0x69, 0x6f, 0x64, 0x5f, 0x6d, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28,
+     0x0d, 0x52, 0x18, 0x75, 0x6e, 0x77, 0x69, 0x6e, 0x64, 0x53, 0x74, 0x61,
+     0x74, 0x65, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x50, 0x65, 0x72, 0x69, 0x6f,
+     0x64, 0x4d, 0x73, 0x22, 0xf3, 0x03, 0x0a, 0x0e, 0x53, 0x79, 0x73, 0x53,
+     0x74, 0x61, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2a,
+     0x0a, 0x11, 0x6d, 0x65, 0x6d, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x70, 0x65,
+     0x72, 0x69, 0x6f, 0x64, 0x5f, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
+     0x0d, 0x52, 0x0f, 0x6d, 0x65, 0x6d, 0x69, 0x6e, 0x66, 0x6f, 0x50, 0x65,
+     0x72, 0x69, 0x6f, 0x64, 0x4d, 0x73, 0x12, 0x4b, 0x0a, 0x10, 0x6d, 0x65,
+     0x6d, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65,
+     0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x70,
+     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x69, 0x6e, 0x66, 0x6f, 0x43, 0x6f,
+     0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x52, 0x0f, 0x6d, 0x65, 0x6d, 0x69,
+     0x6e, 0x66, 0x6f, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12,
+     0x28, 0x0a, 0x10, 0x76, 0x6d, 0x73, 0x74, 0x61, 0x74, 0x5f, 0x70, 0x65,
+     0x72, 0x69, 0x6f, 0x64, 0x5f, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
+     0x0d, 0x52, 0x0e, 0x76, 0x6d, 0x73, 0x74, 0x61, 0x74, 0x50, 0x65, 0x72,
+     0x69, 0x6f, 0x64, 0x4d, 0x73, 0x12, 0x48, 0x0a, 0x0f, 0x76, 0x6d, 0x73,
+     0x74, 0x61, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73,
+     0x18, 0x04, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x70, 0x65, 0x72,
+     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+     0x2e, 0x56, 0x6d, 0x73, 0x74, 0x61, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74,
+     0x65, 0x72, 0x73, 0x52, 0x0e, 0x76, 0x6d, 0x73, 0x74, 0x61, 0x74, 0x43,
+     0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x73,
+     0x74, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x6d,
+     0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x73, 0x74, 0x61,
+     0x74, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x4d, 0x73, 0x12, 0x51, 0x0a,
+     0x0d, 0x73, 0x74, 0x61, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65,
+     0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x70,
+     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x73, 0x2e, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x43,
+     0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x43, 0x6f,
+     0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x74,
+     0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x22, 0x7b, 0x0a, 0x0c,
+     0x53, 0x74, 0x61, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73,
+     0x12, 0x14, 0x0a, 0x10, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x55, 0x4e, 0x53,
+     0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x12,
+     0x0a, 0x0e, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x43, 0x50, 0x55, 0x5f, 0x54,
+     0x49, 0x4d, 0x45, 0x53, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x54,
+     0x41, 0x54, 0x5f, 0x49, 0x52, 0x51, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54,
+     0x53, 0x10, 0x02, 0x12, 0x17, 0x0a, 0x13, 0x53, 0x54, 0x41, 0x54, 0x5f,
+     0x53, 0x4f, 0x46, 0x54, 0x49, 0x52, 0x51, 0x5f, 0x43, 0x4f, 0x55, 0x4e,
+     0x54, 0x53, 0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x54, 0x41, 0x54,
+     0x5f, 0x46, 0x4f, 0x52, 0x4b, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x10,
+     0x04, 0x22, 0x9e, 0x06, 0x0a, 0x0a, 0x54, 0x65, 0x73, 0x74, 0x43, 0x6f,
+     0x6e, 0x66, 0x69, 0x67, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x65, 0x73, 0x73,
+     0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20,
+     0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
+     0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x35, 0x0a, 0x17, 0x6d, 0x61, 0x78,
+     0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x5f, 0x70, 0x65,
+     0x72, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01,
+     0x28, 0x0d, 0x52, 0x14, 0x6d, 0x61, 0x78, 0x4d, 0x65, 0x73, 0x73, 0x61,
+     0x67, 0x65, 0x73, 0x50, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64,
+     0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01,
+     0x28, 0x0d, 0x52, 0x04, 0x73, 0x65, 0x65, 0x64, 0x12, 0x21, 0x0a, 0x0c,
+     0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65,
+     0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6d, 0x65, 0x73, 0x73,
+     0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x33, 0x0a, 0x16, 0x73,
+     0x65, 0x6e, 0x64, 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6f, 0x6e,
+     0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20,
+     0x01, 0x28, 0x08, 0x52, 0x13, 0x73, 0x65, 0x6e, 0x64, 0x42, 0x61, 0x74,
+     0x63, 0x68, 0x4f, 0x6e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72,
+     0x12, 0x4a, 0x0a, 0x0c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x5f, 0x66, 0x69,
+     0x65, 0x6c, 0x64, 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, 0x54, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e,
+     0x66, 0x69, 0x67, 0x2e, 0x44, 0x75, 0x6d, 0x6d, 0x79, 0x46, 0x69, 0x65,
+     0x6c, 0x64, 0x73, 0x52, 0x0b, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x46, 0x69,
+     0x65, 0x6c, 0x64, 0x73, 0x1a, 0xfb, 0x03, 0x0a, 0x0b, 0x44, 0x75, 0x6d,
+     0x6d, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c,
+     0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32,
+     0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x66, 0x69, 0x65, 0x6c,
+     0x64, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x12, 0x1f, 0x0a, 0x0b, 0x66,
+     0x69, 0x65, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x02,
+     0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x49,
+     0x6e, 0x74, 0x33, 0x32, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x69, 0x65, 0x6c,
+     0x64, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x03, 0x20, 0x01,
+     0x28, 0x04, 0x52, 0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x55, 0x69, 0x6e,
+     0x74, 0x36, 0x34, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64,
+     0x5f, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03,
+     0x52, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x49, 0x6e, 0x74, 0x36, 0x34,
+     0x12, 0x23, 0x0a, 0x0d, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x66, 0x69,
+     0x78, 0x65, 0x64, 0x36, 0x34, 0x18, 0x05, 0x20, 0x01, 0x28, 0x06, 0x52,
+     0x0c, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x46, 0x69, 0x78, 0x65, 0x64, 0x36,
+     0x34, 0x12, 0x25, 0x0a, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x73,
+     0x66, 0x69, 0x78, 0x65, 0x64, 0x36, 0x34, 0x18, 0x06, 0x20, 0x01, 0x28,
+     0x10, 0x52, 0x0d, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x66, 0x69, 0x78,
+     0x65, 0x64, 0x36, 0x34, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x69, 0x65, 0x6c,
+     0x64, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x33, 0x32, 0x18, 0x07, 0x20,
+     0x01, 0x28, 0x07, 0x52, 0x0c, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x46, 0x69,
+     0x78, 0x65, 0x64, 0x33, 0x32, 0x12, 0x25, 0x0a, 0x0e, 0x66, 0x69, 0x65,
+     0x6c, 0x64, 0x5f, 0x73, 0x66, 0x69, 0x78, 0x65, 0x64, 0x33, 0x32, 0x18,
+     0x08, 0x20, 0x01, 0x28, 0x0f, 0x52, 0x0d, 0x66, 0x69, 0x65, 0x6c, 0x64,
+     0x53, 0x66, 0x69, 0x78, 0x65, 0x64, 0x33, 0x32, 0x12, 0x21, 0x0a, 0x0c,
+     0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65,
+     0x18, 0x09, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x66, 0x69, 0x65, 0x6c,
+     0x64, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x66,
+     0x69, 0x65, 0x6c, 0x64, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x18, 0x0a,
+     0x20, 0x01, 0x28, 0x02, 0x52, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x46,
+     0x6c, 0x6f, 0x61, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x69, 0x65, 0x6c,
+     0x64, 0x5f, 0x73, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x0b, 0x20, 0x01,
+     0x28, 0x12, 0x52, 0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x69, 0x6e,
+     0x74, 0x36, 0x34, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x69, 0x65, 0x6c, 0x64,
+     0x5f, 0x73, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x0c, 0x20, 0x01, 0x28,
+     0x11, 0x52, 0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x69, 0x6e, 0x74,
+     0x33, 0x32, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f,
+     0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09,
+     0x52, 0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e,
+     0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x62,
+     0x79, 0x74, 0x65, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a,
+     0x66, 0x69, 0x65, 0x6c, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x22, 0xba,
+     0x01, 0x0a, 0x10, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x45, 0x76, 0x65, 0x6e,
+     0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2f, 0x0a, 0x13, 0x64,
+     0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x74, 0x65,
+     0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09,
+     0x52, 0x12, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x43, 0x61,
+     0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x12,
+     0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x74, 0x65,
+     0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09,
+     0x52, 0x11, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x43, 0x61, 0x74,
+     0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x64,
+     0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x74, 0x61, 0x67, 0x73,
+     0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x61,
+     0x62, 0x6c, 0x65, 0x64, 0x54, 0x61, 0x67, 0x73, 0x12, 0x21, 0x0a, 0x0c,
+     0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x74, 0x61, 0x67, 0x73,
+     0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x65, 0x6e, 0x61, 0x62,
+     0x6c, 0x65, 0x64, 0x54, 0x61, 0x67, 0x73, 0x22, 0x84, 0x0c, 0x0a, 0x10,
      0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f,
      0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
      0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
@@ -489,413 +829,187 @@
      0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42,
      0x02, 0x28, 0x01, 0x52, 0x12, 0x76, 0x75, 0x6c, 0x6b, 0x61, 0x6e, 0x4d,
      0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
-     0x42, 0x0a, 0x0d, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x63, 0x6f,
-     0x6e, 0x66, 0x69, 0x67, 0x18, 0x65, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d,
-     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, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x63, 0x68, 0x72, 0x6f, 0x6d,
-     0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x24, 0x0a, 0x0d, 0x6c,
-     0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
-     0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x65, 0x67,
-     0x61, 0x63, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3d, 0x0a,
-     0x0b, 0x66, 0x6f, 0x72, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67,
-     0x18, 0xe9, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x65,
-     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-     0x73, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
-     0x52, 0x0a, 0x66, 0x6f, 0x72, 0x54, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67,
-     0x4a, 0x0b, 0x08, 0xff, 0xff, 0xff, 0x7f, 0x10, 0x80, 0x80, 0x80, 0x80,
-     0x01, 0x22, 0xd6, 0x02, 0x0a, 0x0c, 0x46, 0x74, 0x72, 0x61, 0x63, 0x65,
-     0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x74,
-     0x72, 0x61, 0x63, 0x65, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18,
-     0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x66, 0x74, 0x72, 0x61, 0x63,
-     0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x61,
-     0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f,
-     0x72, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10,
-     0x61, 0x74, 0x72, 0x61, 0x63, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f,
-     0x72, 0x69, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x74, 0x72, 0x61,
-     0x63, 0x65, 0x5f, 0x61, 0x70, 0x70, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,
-     0x09, 0x52, 0x0a, 0x61, 0x74, 0x72, 0x61, 0x63, 0x65, 0x41, 0x70, 0x70,
-     0x73, 0x12, 0x24, 0x0a, 0x0e, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f,
-     0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6b, 0x62, 0x18, 0x0a, 0x20, 0x01, 0x28,
-     0x0d, 0x52, 0x0c, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x53, 0x69, 0x7a,
-     0x65, 0x4b, 0x62, 0x12, 0x26, 0x0a, 0x0f, 0x64, 0x72, 0x61, 0x69, 0x6e,
-     0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x6d, 0x73, 0x18, 0x0b,
-     0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x64, 0x72, 0x61, 0x69, 0x6e, 0x50,
-     0x65, 0x72, 0x69, 0x6f, 0x64, 0x4d, 0x73, 0x12, 0x55, 0x0a, 0x0d, 0x63,
-     0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64,
-     0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x70, 0x65, 0x72,
-     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-     0x2e, 0x46, 0x74, 0x72, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69,
-     0x67, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x53, 0x63, 0x68,
-     0x65, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x63, 0x6f,
-     0x6d, 0x70, 0x61, 0x63, 0x74, 0x53, 0x63, 0x68, 0x65, 0x64, 0x1a, 0x2e,
-     0x0a, 0x12, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x53, 0x63, 0x68,
-     0x65, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07,
-     0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
-     0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x95,
-     0x03, 0x0a, 0x0f, 0x49, 0x6e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x6c, 0x65,
-     0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x63,
-     0x61, 0x6e, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f,
-     0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x73, 0x63,
-     0x61, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x73,
-     0x12, 0x22, 0x0a, 0x0d, 0x73, 0x63, 0x61, 0x6e, 0x5f, 0x64, 0x65, 0x6c,
-     0x61, 0x79, 0x5f, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52,
-     0x0b, 0x73, 0x63, 0x61, 0x6e, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x73,
-     0x12, 0x26, 0x0a, 0x0f, 0x73, 0x63, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x74,
-     0x63, 0x68, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
-     0x0d, 0x52, 0x0d, 0x73, 0x63, 0x61, 0x6e, 0x42, 0x61, 0x74, 0x63, 0x68,
-     0x53, 0x69, 0x7a, 0x65, 0x12, 0x1e, 0x0a, 0x0b, 0x64, 0x6f, 0x5f, 0x6e,
-     0x6f, 0x74, 0x5f, 0x73, 0x63, 0x61, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28,
-     0x08, 0x52, 0x09, 0x64, 0x6f, 0x4e, 0x6f, 0x74, 0x53, 0x63, 0x61, 0x6e,
-     0x12, 0x2a, 0x0a, 0x11, 0x73, 0x63, 0x61, 0x6e, 0x5f, 0x6d, 0x6f, 0x75,
-     0x6e, 0x74, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20,
-     0x03, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x63, 0x61, 0x6e, 0x4d, 0x6f, 0x75,
-     0x6e, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x67, 0x0a, 0x13,
-     0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f,
-     0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x06, 0x20, 0x03, 0x28,
-     0x0b, 0x32, 0x37, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x49, 0x6e, 0x6f, 0x64,
-     0x65, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
-     0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x4d, 0x61,
-     0x70, 0x70, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11,
-     0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x4d, 0x61,
-     0x70, 0x70, 0x69, 0x6e, 0x67, 0x1a, 0x57, 0x0a, 0x16, 0x4d, 0x6f, 0x75,
-     0x6e, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69,
-     0x6e, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x6d,
-     0x6f, 0x75, 0x6e, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20,
-     0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x70, 0x6f,
-     0x69, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x63, 0x61, 0x6e, 0x5f,
-     0x72, 0x6f, 0x6f, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52,
-     0x09, 0x73, 0x63, 0x61, 0x6e, 0x52, 0x6f, 0x6f, 0x74, 0x73, 0x22, 0x81,
-     0x03, 0x0a, 0x12, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x6f,
-     0x77, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x26, 0x0a,
-     0x0f, 0x62, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x5f, 0x70, 0x6f, 0x6c,
-     0x6c, 0x5f, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d,
-     0x62, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x50, 0x6f, 0x6c, 0x6c, 0x4d,
-     0x73, 0x12, 0x5e, 0x0a, 0x10, 0x62, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79,
-     0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20,
-     0x03, 0x28, 0x0e, 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, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x43, 0x6f,
-     0x6e, 0x66, 0x69, 0x67, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79,
-     0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x52, 0x0f, 0x62, 0x61,
-     0x74, 0x74, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72,
-     0x73, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74,
-     0x5f, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x5f, 0x72, 0x61, 0x69, 0x6c, 0x73,
-     0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x63, 0x6f, 0x6c, 0x6c,
-     0x65, 0x63, 0x74, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x61, 0x69, 0x6c,
-     0x73, 0x22, 0xb2, 0x01, 0x0a, 0x0f, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72,
-     0x79, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1f, 0x0a,
-     0x1b, 0x42, 0x41, 0x54, 0x54, 0x45, 0x52, 0x59, 0x5f, 0x43, 0x4f, 0x55,
-     0x4e, 0x54, 0x45, 0x52, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49,
-     0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x42, 0x41,
-     0x54, 0x54, 0x45, 0x52, 0x59, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x45,
-     0x52, 0x5f, 0x43, 0x48, 0x41, 0x52, 0x47, 0x45, 0x10, 0x01, 0x12, 0x24,
-     0x0a, 0x20, 0x42, 0x41, 0x54, 0x54, 0x45, 0x52, 0x59, 0x5f, 0x43, 0x4f,
-     0x55, 0x4e, 0x54, 0x45, 0x52, 0x5f, 0x43, 0x41, 0x50, 0x41, 0x43, 0x49,
-     0x54, 0x59, 0x5f, 0x50, 0x45, 0x52, 0x43, 0x45, 0x4e, 0x54, 0x10, 0x02,
-     0x12, 0x1b, 0x0a, 0x17, 0x42, 0x41, 0x54, 0x54, 0x45, 0x52, 0x59, 0x5f,
-     0x43, 0x4f, 0x55, 0x4e, 0x54, 0x45, 0x52, 0x5f, 0x43, 0x55, 0x52, 0x52,
-     0x45, 0x4e, 0x54, 0x10, 0x03, 0x12, 0x1f, 0x0a, 0x1b, 0x42, 0x41, 0x54,
-     0x54, 0x45, 0x52, 0x59, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x45, 0x52,
-     0x5f, 0x43, 0x55, 0x52, 0x52, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x56, 0x47,
-     0x10, 0x04, 0x22, 0x80, 0x03, 0x0a, 0x12, 0x50, 0x72, 0x6f, 0x63, 0x65,
-     0x73, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69,
-     0x67, 0x12, 0x42, 0x0a, 0x06, 0x71, 0x75, 0x69, 0x72, 0x6b, 0x73, 0x18,
-     0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
-     0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73,
-     0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x51, 0x75, 0x69, 0x72, 0x6b,
-     0x73, 0x52, 0x06, 0x71, 0x75, 0x69, 0x72, 0x6b, 0x73, 0x12, 0x3c, 0x0a,
-     0x1b, 0x73, 0x63, 0x61, 0x6e, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x70, 0x72,
-     0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x5f, 0x6f, 0x6e, 0x5f, 0x73,
-     0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17,
-     0x73, 0x63, 0x61, 0x6e, 0x41, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x63, 0x65,
-     0x73, 0x73, 0x65, 0x73, 0x4f, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12,
-     0x2e, 0x0a, 0x13, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x74, 0x68,
-     0x72, 0x65, 0x61, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x03,
-     0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64,
-     0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12,
-     0x2b, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x63, 0x5f, 0x73, 0x74, 0x61, 0x74,
-     0x73, 0x5f, 0x70, 0x6f, 0x6c, 0x6c, 0x5f, 0x6d, 0x73, 0x18, 0x04, 0x20,
-     0x01, 0x28, 0x0d, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x63, 0x53, 0x74, 0x61,
-     0x74, 0x73, 0x50, 0x6f, 0x6c, 0x6c, 0x4d, 0x73, 0x12, 0x34, 0x0a, 0x17,
-     0x70, 0x72, 0x6f, 0x63, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x5f, 0x63,
-     0x61, 0x63, 0x68, 0x65, 0x5f, 0x74, 0x74, 0x6c, 0x5f, 0x6d, 0x73, 0x18,
-     0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x70, 0x72, 0x6f, 0x63, 0x53,
-     0x74, 0x61, 0x74, 0x73, 0x43, 0x61, 0x63, 0x68, 0x65, 0x54, 0x74, 0x6c,
-     0x4d, 0x73, 0x22, 0x55, 0x0a, 0x06, 0x51, 0x75, 0x69, 0x72, 0x6b, 0x73,
-     0x12, 0x16, 0x0a, 0x12, 0x51, 0x55, 0x49, 0x52, 0x4b, 0x53, 0x5f, 0x55,
-     0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00,
-     0x12, 0x1c, 0x0a, 0x14, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x5f,
-     0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x4c, 0x5f, 0x44, 0x55, 0x4d, 0x50,
-     0x10, 0x01, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x44, 0x49,
-     0x53, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x4d,
-     0x41, 0x4e, 0x44, 0x10, 0x02, 0x22, 0xf3, 0x03, 0x0a, 0x0e, 0x53, 0x79,
-     0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
-     0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x65, 0x6d, 0x69, 0x6e, 0x66, 0x6f, 0x5f,
-     0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x6d, 0x73, 0x18, 0x01, 0x20,
-     0x01, 0x28, 0x0d, 0x52, 0x0f, 0x6d, 0x65, 0x6d, 0x69, 0x6e, 0x66, 0x6f,
-     0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x4d, 0x73, 0x12, 0x4b, 0x0a, 0x10,
-     0x6d, 0x65, 0x6d, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x63, 0x6f, 0x75, 0x6e,
-     0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x20,
-     0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x69, 0x6e, 0x66, 0x6f,
-     0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x52, 0x0f, 0x6d, 0x65,
-     0x6d, 0x69, 0x6e, 0x66, 0x6f, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72,
-     0x73, 0x12, 0x28, 0x0a, 0x10, 0x76, 0x6d, 0x73, 0x74, 0x61, 0x74, 0x5f,
-     0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x6d, 0x73, 0x18, 0x03, 0x20,
-     0x01, 0x28, 0x0d, 0x52, 0x0e, 0x76, 0x6d, 0x73, 0x74, 0x61, 0x74, 0x50,
-     0x65, 0x72, 0x69, 0x6f, 0x64, 0x4d, 0x73, 0x12, 0x48, 0x0a, 0x0f, 0x76,
-     0x6d, 0x73, 0x74, 0x61, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65,
-     0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x70,
-     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x2e, 0x56, 0x6d, 0x73, 0x74, 0x61, 0x74, 0x43, 0x6f, 0x75,
-     0x6e, 0x74, 0x65, 0x72, 0x73, 0x52, 0x0e, 0x76, 0x6d, 0x73, 0x74, 0x61,
-     0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x24, 0x0a,
-     0x0e, 0x73, 0x74, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64,
-     0x5f, 0x6d, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x73,
-     0x74, 0x61, 0x74, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x4d, 0x73, 0x12,
-     0x51, 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e,
-     0x74, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x2c,
-     0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74,
-     0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x53, 0x74, 0x61, 0x74,
-     0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x52, 0x0c, 0x73, 0x74,
-     0x61, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x22, 0x7b,
-     0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65,
-     0x72, 0x73, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x55,
-     0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00,
-     0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x43, 0x50, 0x55,
-     0x5f, 0x54, 0x49, 0x4d, 0x45, 0x53, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f,
-     0x53, 0x54, 0x41, 0x54, 0x5f, 0x49, 0x52, 0x51, 0x5f, 0x43, 0x4f, 0x55,
-     0x4e, 0x54, 0x53, 0x10, 0x02, 0x12, 0x17, 0x0a, 0x13, 0x53, 0x54, 0x41,
-     0x54, 0x5f, 0x53, 0x4f, 0x46, 0x54, 0x49, 0x52, 0x51, 0x5f, 0x43, 0x4f,
-     0x55, 0x4e, 0x54, 0x53, 0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x54,
-     0x41, 0x54, 0x5f, 0x46, 0x4f, 0x52, 0x4b, 0x5f, 0x43, 0x4f, 0x55, 0x4e,
-     0x54, 0x10, 0x04, 0x22, 0x9e, 0x06, 0x0a, 0x0a, 0x54, 0x65, 0x73, 0x74,
-     0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x65,
-     0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18,
-     0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61,
-     0x67, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x35, 0x0a, 0x17, 0x6d,
-     0x61, 0x78, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x5f,
-     0x70, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x18, 0x02,
-     0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, 0x6d, 0x61, 0x78, 0x4d, 0x65, 0x73,
-     0x73, 0x61, 0x67, 0x65, 0x73, 0x50, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f,
-     0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x65, 0x64, 0x18, 0x03,
-     0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x65, 0x65, 0x64, 0x12, 0x21,
-     0x0a, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69,
-     0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6d, 0x65,
-     0x73, 0x73, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x33, 0x0a,
-     0x16, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f,
-     0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x18,
-     0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x73, 0x65, 0x6e, 0x64, 0x42,
-     0x61, 0x74, 0x63, 0x68, 0x4f, 0x6e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74,
-     0x65, 0x72, 0x12, 0x4a, 0x0a, 0x0c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x5f,
-     0x66, 0x69, 0x65, 0x6c, 0x64, 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, 0x54, 0x65, 0x73, 0x74, 0x43,
-     0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x75, 0x6d, 0x6d, 0x79, 0x46,
-     0x69, 0x65, 0x6c, 0x64, 0x73, 0x52, 0x0b, 0x64, 0x75, 0x6d, 0x6d, 0x79,
-     0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0xfb, 0x03, 0x0a, 0x0b, 0x44,
-     0x75, 0x6d, 0x6d, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x21,
-     0x0a, 0x0c, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x75, 0x69, 0x6e, 0x74,
-     0x33, 0x32, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x66, 0x69,
-     0x65, 0x6c, 0x64, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x12, 0x1f, 0x0a,
-     0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x33, 0x32,
-     0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x66, 0x69, 0x65, 0x6c,
-     0x64, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x69,
-     0x65, 0x6c, 0x64, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x03,
-     0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x55,
-     0x69, 0x6e, 0x74, 0x36, 0x34, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x69, 0x65,
-     0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x04, 0x20, 0x01,
-     0x28, 0x03, 0x52, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x49, 0x6e, 0x74,
-     0x36, 0x34, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f,
-     0x66, 0x69, 0x78, 0x65, 0x64, 0x36, 0x34, 0x18, 0x05, 0x20, 0x01, 0x28,
-     0x06, 0x52, 0x0c, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x46, 0x69, 0x78, 0x65,
-     0x64, 0x36, 0x34, 0x12, 0x25, 0x0a, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64,
-     0x5f, 0x73, 0x66, 0x69, 0x78, 0x65, 0x64, 0x36, 0x34, 0x18, 0x06, 0x20,
-     0x01, 0x28, 0x10, 0x52, 0x0d, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x66,
-     0x69, 0x78, 0x65, 0x64, 0x36, 0x34, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x69,
-     0x65, 0x6c, 0x64, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x33, 0x32, 0x18,
-     0x07, 0x20, 0x01, 0x28, 0x07, 0x52, 0x0c, 0x66, 0x69, 0x65, 0x6c, 0x64,
-     0x46, 0x69, 0x78, 0x65, 0x64, 0x33, 0x32, 0x12, 0x25, 0x0a, 0x0e, 0x66,
-     0x69, 0x65, 0x6c, 0x64, 0x5f, 0x73, 0x66, 0x69, 0x78, 0x65, 0x64, 0x33,
-     0x32, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0f, 0x52, 0x0d, 0x66, 0x69, 0x65,
-     0x6c, 0x64, 0x53, 0x66, 0x69, 0x78, 0x65, 0x64, 0x33, 0x32, 0x12, 0x21,
-     0x0a, 0x0c, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x64, 0x6f, 0x75, 0x62,
-     0x6c, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x66, 0x69,
-     0x65, 0x6c, 0x64, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x12, 0x1f, 0x0a,
-     0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74,
-     0x18, 0x0a, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0a, 0x66, 0x69, 0x65, 0x6c,
-     0x64, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x69,
-     0x65, 0x6c, 0x64, 0x5f, 0x73, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x0b,
-     0x20, 0x01, 0x28, 0x12, 0x52, 0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x53,
-     0x69, 0x6e, 0x74, 0x36, 0x34, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x69, 0x65,
-     0x6c, 0x64, 0x5f, 0x73, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x0c, 0x20,
-     0x01, 0x28, 0x11, 0x52, 0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x69,
-     0x6e, 0x74, 0x33, 0x32, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x69, 0x65, 0x6c,
-     0x64, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x0d, 0x20, 0x01,
-     0x28, 0x09, 0x52, 0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x74, 0x72,
-     0x69, 0x6e, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64,
-     0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0c,
-     0x52, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73,
-     0x22, 0x84, 0x1b, 0x0a, 0x0b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43, 0x6f,
-     0x6e, 0x66, 0x69, 0x67, 0x12, 0x43, 0x0a, 0x07, 0x62, 0x75, 0x66, 0x66,
-     0x65, 0x72, 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, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e,
-     0x66, 0x69, 0x67, 0x2e, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x43, 0x6f,
-     0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72,
-     0x73, 0x12, 0x4a, 0x0a, 0x0c, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x6f,
-     0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
-     0x27, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43,
-     0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f,
-     0x75, 0x72, 0x63, 0x65, 0x52, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x53, 0x6f,
-     0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x60, 0x0a, 0x14, 0x62, 0x75, 0x69,
-     0x6c, 0x74, 0x69, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x6f,
-     0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32,
-     0x2e, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43,
-     0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x74, 0x69,
-     0x6e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52,
-     0x12, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61,
-     0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x64,
-     0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x73, 0x18, 0x03,
-     0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69,
-     0x6f, 0x6e, 0x4d, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x65, 0x6e, 0x61, 0x62,
-     0x6c, 0x65, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x67, 0x75, 0x61,
-     0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28,
-     0x08, 0x52, 0x15, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x74,
-     0x72, 0x61, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x73,
-     0x12, 0x57, 0x0a, 0x0d, 0x6c, 0x6f, 0x63, 0x6b, 0x64, 0x6f, 0x77, 0x6e,
-     0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32,
-     0x32, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43,
-     0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x4c, 0x6f, 0x63, 0x6b, 0x64, 0x6f,
-     0x77, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74,
-     0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x6c, 0x6f, 0x63, 0x6b, 0x64, 0x6f, 0x77,
-     0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x49, 0x0a, 0x09, 0x70, 0x72, 0x6f,
-     0x64, 0x75, 0x63, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b,
-     0x32, 0x2b, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65,
-     0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x50, 0x72, 0x6f, 0x64, 0x75,
-     0x63, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x70,
-     0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x72, 0x73, 0x12, 0x54, 0x0a, 0x0f,
-     0x73, 0x74, 0x61, 0x74, 0x73, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64,
-     0x61, 0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e,
-     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e,
-     0x66, 0x69, 0x67, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x64, 0x4d, 0x65,
-     0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0e, 0x73, 0x74, 0x61, 0x74,
-     0x73, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x26,
-     0x0a, 0x0f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x6f,
-     0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52,
-     0x0d, 0x77, 0x72, 0x69, 0x74, 0x65, 0x49, 0x6e, 0x74, 0x6f, 0x46, 0x69,
-     0x6c, 0x65, 0x12, 0x2f, 0x0a, 0x14, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x77,
-     0x72, 0x69, 0x74, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f,
-     0x6d, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x66, 0x69,
-     0x6c, 0x65, 0x57, 0x72, 0x69, 0x74, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f,
-     0x64, 0x4d, 0x73, 0x12, 0x2d, 0x0a, 0x13, 0x6d, 0x61, 0x78, 0x5f, 0x66,
-     0x69, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74,
-     0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x6d, 0x61,
-     0x78, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74,
-     0x65, 0x73, 0x12, 0x60, 0x0a, 0x13, 0x67, 0x75, 0x61, 0x72, 0x64, 0x72,
-     0x61, 0x69, 0x6c, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65,
-     0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x70, 0x65,
-     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-     0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69,
-     0x67, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x4f,
-     0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x73, 0x52, 0x12, 0x67, 0x75,
-     0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x4f, 0x76, 0x65, 0x72, 0x72,
-     0x69, 0x64, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x65,
-     0x72, 0x72, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x0c,
-     0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x64, 0x65, 0x66, 0x65, 0x72, 0x72,
-     0x65, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x66,
-     0x6c, 0x75, 0x73, 0x68, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f,
-     0x6d, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x66, 0x6c,
-     0x75, 0x73, 0x68, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x4d, 0x73, 0x12,
-     0x28, 0x0a, 0x10, 0x66, 0x6c, 0x75, 0x73, 0x68, 0x5f, 0x74, 0x69, 0x6d,
-     0x65, 0x6f, 0x75, 0x74, 0x5f, 0x6d, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28,
-     0x0d, 0x52, 0x0e, 0x66, 0x6c, 0x75, 0x73, 0x68, 0x54, 0x69, 0x6d, 0x65,
-     0x6f, 0x75, 0x74, 0x4d, 0x73, 0x12, 0x3c, 0x0a, 0x1b, 0x64, 0x61, 0x74,
-     0x61, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x6f,
-     0x70, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x6d, 0x73,
-     0x18, 0x17, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x17, 0x64, 0x61, 0x74, 0x61,
-     0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x6f, 0x70, 0x54, 0x69,
-     0x6d, 0x65, 0x6f, 0x75, 0x74, 0x4d, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6e,
-     0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x75,
-     0x72, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6e, 0x6f, 0x74,
-     0x69, 0x66, 0x79, 0x54, 0x72, 0x61, 0x63, 0x65, 0x75, 0x72, 0x12, 0x51,
-     0x0a, 0x0e, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x5f, 0x63, 0x6f,
-     0x6e, 0x66, 0x69, 0x67, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a,
-     0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43, 0x6f,
-     0x6e, 0x66, 0x69, 0x67, 0x2e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72,
-     0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x74, 0x72, 0x69, 0x67,
-     0x67, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2b, 0x0a,
-     0x11, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x72,
-     0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x18, 0x12, 0x20, 0x03, 0x28, 0x09,
-     0x52, 0x10, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x54, 0x72,
-     0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x12, 0x6d, 0x0a, 0x18, 0x69, 0x6e,
-     0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x5f, 0x73, 0x74,
-     0x61, 0x74, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x15,
-     0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
-     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54,
-     0x72, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x49,
-     0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x53, 0x74,
-     0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x16, 0x69,
-     0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x53, 0x74,
-     0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x37, 0x0a,
-     0x18, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f,
-     0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x69, 0x6e,
-     0x67, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x61, 0x6c, 0x6c,
-     0x6f, 0x77, 0x55, 0x73, 0x65, 0x72, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x54,
-     0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x12, 0x2e, 0x0a, 0x13, 0x75, 0x6e,
-     0x69, 0x71, 0x75, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
-     0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52,
-     0x11, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69,
-     0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x57, 0x0a, 0x10, 0x63, 0x6f,
-     0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79,
-     0x70, 0x65, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x70,
-     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66,
-     0x69, 0x67, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69,
-     0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x70,
-     0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12,
-     0x67, 0x0a, 0x16, 0x69, 0x6e, 0x63, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x5f,
-     0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
-     0x67, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x70, 0x65,
-     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-     0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69,
-     0x67, 0x2e, 0x49, 0x6e, 0x63, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x65,
-     0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x14,
-     0x69, 0x6e, 0x63, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f,
-     0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x24, 0x0a, 0x0e,
-     0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x5f, 0x6d,
-     0x73, 0x62, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x74, 0x72,
-     0x61, 0x63, 0x65, 0x55, 0x75, 0x69, 0x64, 0x4d, 0x73, 0x62, 0x12, 0x24,
-     0x0a, 0x0e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64,
-     0x5f, 0x6c, 0x73, 0x62, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c,
-     0x74, 0x72, 0x61, 0x63, 0x65, 0x55, 0x75, 0x69, 0x64, 0x4c, 0x73, 0x62,
-     0x1a, 0xc7, 0x01, 0x0a, 0x0c, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x43,
-     0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x69, 0x7a,
-     0x65, 0x5f, 0x6b, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06,
-     0x73, 0x69, 0x7a, 0x65, 0x4b, 0x62, 0x12, 0x55, 0x0a, 0x0b, 0x66, 0x69,
-     0x6c, 0x6c, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x04, 0x20,
-     0x01, 0x28, 0x0e, 0x32, 0x34, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x53, 0x0a, 0x12, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x65, 0x76, 0x65,
+     0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x71, 0x20,
+     0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
      0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72,
-     0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x42, 0x75,
-     0x66, 0x66, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x46,
-     0x69, 0x6c, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0a, 0x66,
-     0x69, 0x6c, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x3b, 0x0a,
-     0x0a, 0x46, 0x69, 0x6c, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12,
-     0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49,
-     0x45, 0x44, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x49, 0x4e, 0x47,
-     0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x10, 0x01, 0x12, 0x0b, 0x0a,
-     0x07, 0x44, 0x49, 0x53, 0x43, 0x41, 0x52, 0x44, 0x10, 0x02, 0x4a, 0x04,
-     0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x1a, 0x79,
+     0x61, 0x63, 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66,
+     0x69, 0x67, 0x42, 0x02, 0x28, 0x01, 0x52, 0x10, 0x74, 0x72, 0x61, 0x63,
+     0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+     0x12, 0x42, 0x0a, 0x0d, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x63,
+     0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x65, 0x20, 0x01, 0x28, 0x0b, 0x32,
+     0x1d, 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, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x63, 0x68, 0x72, 0x6f,
+     0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x24, 0x0a, 0x0d,
+     0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
+     0x67, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x65,
+     0x67, 0x61, 0x63, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3d,
+     0x0a, 0x0b, 0x66, 0x6f, 0x72, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e,
+     0x67, 0x18, 0xe9, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70,
+     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x73, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69,
+     0x67, 0x52, 0x0a, 0x66, 0x6f, 0x72, 0x54, 0x65, 0x73, 0x74, 0x69, 0x6e,
+     0x67, 0x4a, 0x0b, 0x08, 0xff, 0xff, 0xff, 0x7f, 0x10, 0x80, 0x80, 0x80,
+     0x80, 0x01, 0x22, 0x99, 0x1c, 0x0a, 0x0b, 0x54, 0x72, 0x61, 0x63, 0x65,
+     0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x43, 0x0a, 0x07, 0x62, 0x75,
+     0x66, 0x66, 0x65, 0x72, 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, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43,
+     0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72,
+     0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x62, 0x75, 0x66, 0x66,
+     0x65, 0x72, 0x73, 0x12, 0x4a, 0x0a, 0x0c, 0x64, 0x61, 0x74, 0x61, 0x5f,
+     0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
+     0x0b, 0x32, 0x27, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63,
+     0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x61, 0x74, 0x61,
+     0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0b, 0x64, 0x61, 0x74, 0x61,
+     0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x60, 0x0a, 0x14, 0x62,
+     0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f,
+     0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28,
+     0x0b, 0x32, 0x2e, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63,
+     0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x42, 0x75, 0x69, 0x6c,
+     0x74, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63,
+     0x65, 0x52, 0x12, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x44, 0x61,
+     0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x1f, 0x0a,
+     0x0b, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x73,
+     0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x64, 0x75, 0x72, 0x61,
+     0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x65, 0x6e,
+     0x61, 0x62, 0x6c, 0x65, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x67,
+     0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x04, 0x20,
+     0x01, 0x28, 0x08, 0x52, 0x15, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x45,
+     0x78, 0x74, 0x72, 0x61, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69,
+     0x6c, 0x73, 0x12, 0x57, 0x0a, 0x0d, 0x6c, 0x6f, 0x63, 0x6b, 0x64, 0x6f,
+     0x77, 0x6e, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28,
+     0x0e, 0x32, 0x32, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63,
+     0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x4c, 0x6f, 0x63, 0x6b,
+     0x64, 0x6f, 0x77, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x4f, 0x70, 0x65, 0x72,
+     0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x6c, 0x6f, 0x63, 0x6b, 0x64,
+     0x6f, 0x77, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x49, 0x0a, 0x09, 0x70,
+     0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03,
+     0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61,
+     0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x50, 0x72, 0x6f,
+     0x64, 0x75, 0x63, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52,
+     0x09, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x72, 0x73, 0x12, 0x54,
+     0x0a, 0x0f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x64, 0x5f, 0x6d, 0x65, 0x74,
+     0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32,
+     0x2b, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43,
+     0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x64,
+     0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0e, 0x73, 0x74,
+     0x61, 0x74, 0x73, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
+     0x12, 0x26, 0x0a, 0x0f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x69, 0x6e,
+     0x74, 0x6f, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28,
+     0x08, 0x52, 0x0d, 0x77, 0x72, 0x69, 0x74, 0x65, 0x49, 0x6e, 0x74, 0x6f,
+     0x46, 0x69, 0x6c, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x75, 0x74, 0x70,
+     0x75, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x1d, 0x20, 0x01, 0x28,
+     0x09, 0x52, 0x0a, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x50, 0x61, 0x74,
+     0x68, 0x12, 0x2f, 0x0a, 0x14, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x77, 0x72,
+     0x69, 0x74, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x6d,
+     0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x66, 0x69, 0x6c,
+     0x65, 0x57, 0x72, 0x69, 0x74, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64,
+     0x4d, 0x73, 0x12, 0x2d, 0x0a, 0x13, 0x6d, 0x61, 0x78, 0x5f, 0x66, 0x69,
+     0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65,
+     0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x6d, 0x61, 0x78,
+     0x46, 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74, 0x65,
+     0x73, 0x12, 0x60, 0x0a, 0x13, 0x67, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61,
+     0x69, 0x6c, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x73,
+     0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x70, 0x65, 0x72,
+     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+     0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+     0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x4f, 0x76,
+     0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x73, 0x52, 0x12, 0x67, 0x75, 0x61,
+     0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69,
+     0x64, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x65, 0x72,
+     0x72, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x0c, 0x20,
+     0x01, 0x28, 0x08, 0x52, 0x0d, 0x64, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65,
+     0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x66, 0x6c,
+     0x75, 0x73, 0x68, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x6d,
+     0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x66, 0x6c, 0x75,
+     0x73, 0x68, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x4d, 0x73, 0x12, 0x28,
+     0x0a, 0x10, 0x66, 0x6c, 0x75, 0x73, 0x68, 0x5f, 0x74, 0x69, 0x6d, 0x65,
+     0x6f, 0x75, 0x74, 0x5f, 0x6d, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d,
+     0x52, 0x0e, 0x66, 0x6c, 0x75, 0x73, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x6f,
+     0x75, 0x74, 0x4d, 0x73, 0x12, 0x3c, 0x0a, 0x1b, 0x64, 0x61, 0x74, 0x61,
+     0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x6f, 0x70,
+     0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x6d, 0x73, 0x18,
+     0x17, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x17, 0x64, 0x61, 0x74, 0x61, 0x53,
+     0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x6f, 0x70, 0x54, 0x69, 0x6d,
+     0x65, 0x6f, 0x75, 0x74, 0x4d, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x6f,
+     0x74, 0x69, 0x66, 0x79, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x75, 0x72,
+     0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6e, 0x6f, 0x74, 0x69,
+     0x66, 0x79, 0x54, 0x72, 0x61, 0x63, 0x65, 0x75, 0x72, 0x12, 0x51, 0x0a,
+     0x0e, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e,
+     0x66, 0x69, 0x67, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e,
+     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e,
+     0x66, 0x69, 0x67, 0x2e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43,
+     0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x74, 0x72, 0x69, 0x67, 0x67,
+     0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2b, 0x0a, 0x11,
+     0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x72, 0x69,
+     0x67, 0x67, 0x65, 0x72, 0x73, 0x18, 0x12, 0x20, 0x03, 0x28, 0x09, 0x52,
+     0x10, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x54, 0x72, 0x69,
+     0x67, 0x67, 0x65, 0x72, 0x73, 0x12, 0x6d, 0x0a, 0x18, 0x69, 0x6e, 0x63,
+     0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x61,
+     0x74, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x15, 0x20,
+     0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72,
+     0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x49, 0x6e,
+     0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x53, 0x74, 0x61,
+     0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x16, 0x69, 0x6e,
+     0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x53, 0x74, 0x61,
+     0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x37, 0x0a, 0x18,
+     0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x62,
+     0x75, 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67,
+     0x18, 0x13, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x61, 0x6c, 0x6c, 0x6f,
+     0x77, 0x55, 0x73, 0x65, 0x72, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x54, 0x72,
+     0x61, 0x63, 0x69, 0x6e, 0x67, 0x12, 0x2e, 0x0a, 0x13, 0x75, 0x6e, 0x69,
+     0x71, 0x75, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f,
+     0x6e, 0x61, 0x6d, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11,
+     0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f,
+     0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x57, 0x0a, 0x10, 0x63, 0x6f, 0x6d,
+     0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70,
+     0x65, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x70, 0x65,
+     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69,
+     0x67, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f,
+     0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x72,
+     0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x67,
+     0x0a, 0x16, 0x69, 0x6e, 0x63, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x5f, 0x72,
+     0x65, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+     0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x70, 0x65, 0x72,
+     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+     0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+     0x2e, 0x49, 0x6e, 0x63, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70,
+     0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x14, 0x69,
+     0x6e, 0x63, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72,
+     0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x24, 0x0a, 0x0e, 0x74,
+     0x72, 0x61, 0x63, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x5f, 0x6d, 0x73,
+     0x62, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x74, 0x72, 0x61,
+     0x63, 0x65, 0x55, 0x75, 0x69, 0x64, 0x4d, 0x73, 0x62, 0x12, 0x24, 0x0a,
+     0x0e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x5f,
+     0x6c, 0x73, 0x62, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x74,
+     0x72, 0x61, 0x63, 0x65, 0x55, 0x75, 0x69, 0x64, 0x4c, 0x73, 0x62, 0x1a,
+     0xc7, 0x01, 0x0a, 0x0c, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x43, 0x6f,
+     0x6e, 0x66, 0x69, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x69, 0x7a, 0x65,
+     0x5f, 0x6b, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73,
+     0x69, 0x7a, 0x65, 0x4b, 0x62, 0x12, 0x55, 0x0a, 0x0b, 0x66, 0x69, 0x6c,
+     0x6c, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01,
+     0x28, 0x0e, 0x32, 0x34, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61,
+     0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x42, 0x75, 0x66,
+     0x66, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x46, 0x69,
+     0x6c, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0a, 0x66, 0x69,
+     0x6c, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x3b, 0x0a, 0x0a,
+     0x46, 0x69, 0x6c, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x0f,
+     0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45,
+     0x44, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x49, 0x4e, 0x47, 0x5f,
+     0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07,
+     0x44, 0x49, 0x53, 0x43, 0x41, 0x52, 0x44, 0x10, 0x02, 0x4a, 0x04, 0x08,
+     0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x1a, 0xb6, 0x01,
      0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65,
      0x12, 0x39, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01,
      0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
@@ -906,571 +1020,463 @@
      0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18,
      0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x70, 0x72, 0x6f, 0x64, 0x75,
      0x63, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65,
-     0x72, 0x1a, 0xb3, 0x01, 0x0a, 0x11, 0x42, 0x75, 0x69, 0x6c, 0x74, 0x69,
-     0x6e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12,
-     0x3c, 0x0a, 0x1a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63,
-     0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f,
-     0x74, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
-     0x18, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6c, 0x6f, 0x63,
-     0x6b, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x74, 0x69, 0x6e,
-     0x67, 0x12, 0x30, 0x0a, 0x14, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65,
-     0x5f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
-     0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x64, 0x69, 0x73,
-     0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e,
-     0x66, 0x69, 0x67, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62,
-     0x6c, 0x65, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x6e,
-     0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69,
-     0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49,
-     0x6e, 0x66, 0x6f, 0x1a, 0x77, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x64, 0x75,
-     0x63, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x23, 0x0a,
-     0x0d, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x72, 0x5f, 0x6e, 0x61,
-     0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72,
-     0x6f, 0x64, 0x75, 0x63, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e,
-     0x0a, 0x0b, 0x73, 0x68, 0x6d, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6b,
-     0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x73, 0x68, 0x6d,
-     0x53, 0x69, 0x7a, 0x65, 0x4b, 0x62, 0x12, 0x20, 0x0a, 0x0c, 0x70, 0x61,
-     0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6b, 0x62, 0x18, 0x03,
-     0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69,
-     0x7a, 0x65, 0x4b, 0x62, 0x1a, 0xe4, 0x01, 0x0a, 0x0e, 0x53, 0x74, 0x61,
-     0x74, 0x73, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12,
-     0x2e, 0x0a, 0x13, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x69, 0x6e,
-     0x67, 0x5f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01,
-     0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65,
-     0x72, 0x69, 0x6e, 0x67, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x49, 0x64, 0x12,
-     0x32, 0x0a, 0x15, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x69, 0x6e,
-     0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x75, 0x69, 0x64,
-     0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x13, 0x74, 0x72, 0x69, 0x67,
-     0x67, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
-     0x55, 0x69, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x74, 0x72, 0x69, 0x67, 0x67,
-     0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
-     0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x74,
-     0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e,
-     0x66, 0x69, 0x67, 0x49, 0x64, 0x12, 0x3c, 0x0a, 0x1a, 0x74, 0x72, 0x69,
-     0x67, 0x67, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x75, 0x62, 0x73,
-     0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18,
-     0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x18, 0x74, 0x72, 0x69, 0x67, 0x67,
-     0x65, 0x72, 0x69, 0x6e, 0x67, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69,
-     0x70, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x1a, 0x4c, 0x0a, 0x12, 0x47,
-     0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x4f, 0x76, 0x65, 0x72,
-     0x72, 0x69, 0x64, 0x65, 0x73, 0x12, 0x36, 0x0a, 0x18, 0x6d, 0x61, 0x78,
-     0x5f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x70, 0x65, 0x72, 0x5f,
-     0x64, 0x61, 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20,
-     0x01, 0x28, 0x04, 0x52, 0x14, 0x6d, 0x61, 0x78, 0x55, 0x70, 0x6c, 0x6f,
-     0x61, 0x64, 0x50, 0x65, 0x72, 0x44, 0x61, 0x79, 0x42, 0x79, 0x74, 0x65,
-     0x73, 0x1a, 0xa0, 0x03, 0x0a, 0x0d, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65,
-     0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x59, 0x0a, 0x0c, 0x74,
-     0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18,
-     0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x36, 0x2e, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
-     0x54, 0x72, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
-     0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69,
-     0x67, 0x2e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x4d, 0x6f, 0x64,
-     0x65, 0x52, 0x0b, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x4d, 0x6f,
-     0x64, 0x65, 0x12, 0x4e, 0x0a, 0x08, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65,
-     0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x70,
-     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66,
-     0x69, 0x67, 0x2e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43, 0x6f,
-     0x6e, 0x66, 0x69, 0x67, 0x2e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72,
-     0x52, 0x08, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x12, 0x2c,
-     0x0a, 0x12, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x5f, 0x74, 0x69,
-     0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01,
-     0x28, 0x0d, 0x52, 0x10, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x54,
-     0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x4d, 0x73, 0x1a, 0x71, 0x0a, 0x07,
-     0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e,
-     0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
-     0x61, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x72, 0x6f, 0x64, 0x75,
-     0x63, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x72, 0x65, 0x67,
-     0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x72,
-     0x6f, 0x64, 0x75, 0x63, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65,
-     0x67, 0x65, 0x78, 0x12, 0x22, 0x0a, 0x0d, 0x73, 0x74, 0x6f, 0x70, 0x5f,
-     0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01,
-     0x28, 0x0d, 0x52, 0x0b, 0x73, 0x74, 0x6f, 0x70, 0x44, 0x65, 0x6c, 0x61,
-     0x79, 0x4d, 0x73, 0x22, 0x43, 0x0a, 0x0b, 0x54, 0x72, 0x69, 0x67, 0x67,
-     0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e,
-     0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12,
-     0x11, 0x0a, 0x0d, 0x53, 0x54, 0x41, 0x52, 0x54, 0x5f, 0x54, 0x52, 0x41,
-     0x43, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x54,
-     0x4f, 0x50, 0x5f, 0x54, 0x52, 0x41, 0x43, 0x49, 0x4e, 0x47, 0x10, 0x02,
-     0x1a, 0x40, 0x0a, 0x16, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e,
-     0x74, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66,
-     0x69, 0x67, 0x12, 0x26, 0x0a, 0x0f, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x5f,
-     0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x6d, 0x73, 0x18, 0x01, 0x20,
-     0x01, 0x28, 0x0d, 0x52, 0x0d, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x50, 0x65,
-     0x72, 0x69, 0x6f, 0x64, 0x4d, 0x73, 0x1a, 0xbc, 0x01, 0x0a, 0x14, 0x49,
-     0x6e, 0x63, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72,
-     0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2f, 0x0a, 0x13, 0x64,
-     0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70,
-     0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
-     0x52, 0x12, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f,
-     0x6e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x2b, 0x0a, 0x11,
-     0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
-     0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
-     0x10, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e,
-     0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x69,
-     0x76, 0x61, 0x63, 0x79, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03,
-     0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x70, 0x72, 0x69, 0x76, 0x61, 0x63,
-     0x79, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x6b,
-     0x69, 0x70, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x62, 0x6f, 0x78, 0x18, 0x04,
-     0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x73, 0x6b, 0x69, 0x70, 0x44, 0x72,
-     0x6f, 0x70, 0x62, 0x6f, 0x78, 0x22, 0x55, 0x0a, 0x15, 0x4c, 0x6f, 0x63,
-     0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x4f, 0x70, 0x65,
-     0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x12, 0x4c, 0x4f,
-     0x43, 0x4b, 0x44, 0x4f, 0x57, 0x4e, 0x5f, 0x55, 0x4e, 0x43, 0x48, 0x41,
-     0x4e, 0x47, 0x45, 0x44, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x4c, 0x4f,
-     0x43, 0x4b, 0x44, 0x4f, 0x57, 0x4e, 0x5f, 0x43, 0x4c, 0x45, 0x41, 0x52,
-     0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4c, 0x4f, 0x43, 0x4b, 0x44, 0x4f,
-     0x57, 0x4e, 0x5f, 0x53, 0x45, 0x54, 0x10, 0x02, 0x22, 0x51, 0x0a, 0x0f,
-     0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54,
-     0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x1c, 0x43, 0x4f, 0x4d, 0x50, 0x52,
-     0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f,
-     0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10,
-     0x00, 0x12, 0x1c, 0x0a, 0x18, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53,
-     0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45,
-     0x46, 0x4c, 0x41, 0x54, 0x45, 0x10, 0x01, 0x4a, 0x04, 0x08, 0x0f, 0x10,
-     0x10, 0x4a, 0x04, 0x08, 0x1a, 0x10, 0x1b, 0x22, 0xa4, 0x05, 0x0a, 0x0f,
-     0x48, 0x65, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x66, 0x64, 0x43, 0x6f, 0x6e,
-     0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x17, 0x73, 0x61, 0x6d, 0x70, 0x6c,
-     0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c,
-     0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04,
-     0x52, 0x15, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x49, 0x6e,
-     0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12,
-     0x27, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63,
-     0x6d, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09,
-     0x52, 0x0e, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6d, 0x64,
-     0x6c, 0x69, 0x6e, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18,
-     0x04, 0x20, 0x03, 0x28, 0x04, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x10,
-     0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52,
-     0x03, 0x61, 0x6c, 0x6c, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x6b, 0x69, 0x70,
-     0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x5f, 0x70, 0x72, 0x65, 0x66,
-     0x69, 0x78, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x73, 0x6b,
-     0x69, 0x70, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x50, 0x72, 0x65, 0x66,
-     0x69, 0x78, 0x12, 0x6b, 0x0a, 0x16, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e,
-     0x75, 0x6f, 0x75, 0x73, 0x5f, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x63, 0x6f,
-     0x6e, 0x66, 0x69, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35,
-     0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x48, 0x65, 0x61, 0x70, 0x70, 0x72, 0x6f,
-     0x66, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x6f, 0x6e,
-     0x74, 0x69, 0x6e, 0x75, 0x6f, 0x75, 0x73, 0x44, 0x75, 0x6d, 0x70, 0x43,
-     0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x14, 0x63, 0x6f, 0x6e, 0x74, 0x69,
-     0x6e, 0x75, 0x6f, 0x75, 0x73, 0x44, 0x75, 0x6d, 0x70, 0x43, 0x6f, 0x6e,
-     0x66, 0x69, 0x67, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x68, 0x6d, 0x65, 0x6d,
-     0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18,
-     0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x73, 0x68, 0x6d, 0x65, 0x6d,
-     0x53, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x21, 0x0a,
-     0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e,
-     0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x62, 0x6c, 0x6f,
-     0x63, 0x6b, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x35, 0x0a, 0x17,
-     0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
-     0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x75, 0x73, 0x18,
-     0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
-     0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75,
-     0x74, 0x55, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x6f, 0x5f, 0x73, 0x74,
-     0x61, 0x72, 0x74, 0x75, 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52,
-     0x09, 0x6e, 0x6f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x12, 0x1d,
-     0x0a, 0x0a, 0x6e, 0x6f, 0x5f, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67,
-     0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6e, 0x6f, 0x52, 0x75,
-     0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x64, 0x6c,
-     0x65, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
-     0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x69, 0x64, 0x6c,
-     0x65, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
-     0x12, 0x1e, 0x0a, 0x0b, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x61, 0x74, 0x5f,
-     0x6d, 0x61, 0x78, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x64,
-     0x75, 0x6d, 0x70, 0x41, 0x74, 0x4d, 0x61, 0x78, 0x1a, 0x64, 0x0a, 0x14,
-     0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x6f, 0x75, 0x73, 0x44, 0x75,
-     0x6d, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x22, 0x0a, 0x0d,
-     0x64, 0x75, 0x6d, 0x70, 0x5f, 0x70, 0x68, 0x61, 0x73, 0x65, 0x5f, 0x6d,
-     0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x64, 0x75, 0x6d,
-     0x70, 0x50, 0x68, 0x61, 0x73, 0x65, 0x4d, 0x73, 0x12, 0x28, 0x0a, 0x10,
-     0x64, 0x75, 0x6d, 0x70, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61,
-     0x6c, 0x5f, 0x6d, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e,
-     0x64, 0x75, 0x6d, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c,
-     0x4d, 0x73, 0x22, 0x9f, 0x02, 0x0a, 0x0f, 0x4a, 0x61, 0x76, 0x61, 0x48,
-     0x70, 0x72, 0x6f, 0x66, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x27,
-     0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6d,
-     0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52,
-     0x0e, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6d, 0x64, 0x6c,
-     0x69, 0x6e, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x02,
-     0x20, 0x03, 0x28, 0x04, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x6b, 0x0a,
-     0x16, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x6f, 0x75, 0x73, 0x5f,
-     0x64, 0x75, 0x6d, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18,
-     0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
-     0x4a, 0x61, 0x76, 0x61, 0x48, 0x70, 0x72, 0x6f, 0x66, 0x43, 0x6f, 0x6e,
-     0x66, 0x69, 0x67, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x6f,
-     0x75, 0x73, 0x44, 0x75, 0x6d, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
-     0x52, 0x14, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x6f, 0x75, 0x73,
-     0x44, 0x75, 0x6d, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x64,
-     0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x6f, 0x75, 0x73,
-     0x44, 0x75, 0x6d, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x22,
-     0x0a, 0x0d, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x70, 0x68, 0x61, 0x73, 0x65,
-     0x5f, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x64,
-     0x75, 0x6d, 0x70, 0x50, 0x68, 0x61, 0x73, 0x65, 0x4d, 0x73, 0x12, 0x28,
-     0x0a, 0x10, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72,
-     0x76, 0x61, 0x6c, 0x5f, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d,
-     0x52, 0x0e, 0x64, 0x75, 0x6d, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76,
-     0x61, 0x6c, 0x4d, 0x73, 0x22, 0x23, 0x0a, 0x0f, 0x50, 0x65, 0x72, 0x66,
-     0x45, 0x76, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
-     0x10, 0x0a, 0x03, 0x74, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05,
-     0x52, 0x03, 0x74, 0x69, 0x64, 0x22, 0xb8, 0x01, 0x0a, 0x10, 0x47, 0x70,
-     0x75, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66,
-     0x69, 0x67, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65,
-     0x72, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x6e, 0x73, 0x18,
-     0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x63, 0x6f, 0x75, 0x6e, 0x74,
-     0x65, 0x72, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x4e, 0x73, 0x12, 0x1f,
-     0x0a, 0x0b, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64,
-     0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x6f, 0x75,
-     0x6e, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x33, 0x0a, 0x15, 0x69,
-     0x6e, 0x73, 0x74, 0x72, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x5f,
-     0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01,
-     0x28, 0x08, 0x52, 0x14, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x6d, 0x65,
-     0x6e, 0x74, 0x65, 0x64, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67,
-     0x12, 0x22, 0x0a, 0x0d, 0x66, 0x69, 0x78, 0x5f, 0x67, 0x70, 0x75, 0x5f,
-     0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52,
-     0x0b, 0x66, 0x69, 0x78, 0x47, 0x70, 0x75, 0x43, 0x6c, 0x6f, 0x63, 0x6b,
-     0x22, 0x8a, 0x01, 0x0a, 0x12, 0x56, 0x75, 0x6c, 0x6b, 0x61, 0x6e, 0x4d,
-     0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
-     0x39, 0x0a, 0x19, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x64, 0x72, 0x69,
-     0x76, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75,
-     0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16,
-     0x74, 0x72, 0x61, 0x63, 0x6b, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x4d,
-     0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x39,
-     0x0a, 0x19, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x76, 0x69,
-     0x63, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73,
-     0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x74,
-     0x72, 0x61, 0x63, 0x6b, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65,
-     0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x61, 0x67, 0x65, 0x22, 0x44, 0x0a,
-     0x12, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x4c, 0x69, 0x73,
-     0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x0a, 0x13, 0x70,
-     0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f,
-     0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09,
-     0x52, 0x11, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d,
-     0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2a, 0x8e, 0x01, 0x0a, 0x0c,
-     0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4c, 0x6f, 0x67, 0x49, 0x64,
-     0x12, 0x0f, 0x0a, 0x0b, 0x4c, 0x49, 0x44, 0x5f, 0x44, 0x45, 0x46, 0x41,
-     0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4c, 0x49, 0x44,
-     0x5f, 0x52, 0x41, 0x44, 0x49, 0x4f, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a,
-     0x4c, 0x49, 0x44, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x53, 0x10, 0x02,
-     0x12, 0x0e, 0x0a, 0x0a, 0x4c, 0x49, 0x44, 0x5f, 0x53, 0x59, 0x53, 0x54,
-     0x45, 0x4d, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x4c, 0x49, 0x44, 0x5f,
-     0x43, 0x52, 0x41, 0x53, 0x48, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x4c,
-     0x49, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x53, 0x10, 0x05, 0x12, 0x10,
-     0x0a, 0x0c, 0x4c, 0x49, 0x44, 0x5f, 0x53, 0x45, 0x43, 0x55, 0x52, 0x49,
-     0x54, 0x59, 0x10, 0x06, 0x12, 0x0e, 0x0a, 0x0a, 0x4c, 0x49, 0x44, 0x5f,
-     0x4b, 0x45, 0x52, 0x4e, 0x45, 0x4c, 0x10, 0x07, 0x2a, 0x9b, 0x01, 0x0a,
-     0x12, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4c, 0x6f, 0x67, 0x50,
-     0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, 0x10, 0x50,
-     0x52, 0x49, 0x4f, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46,
-     0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x52, 0x49,
-     0x4f, 0x5f, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x10, 0x01, 0x12, 0x10,
-     0x0a, 0x0c, 0x50, 0x52, 0x49, 0x4f, 0x5f, 0x56, 0x45, 0x52, 0x42, 0x4f,
-     0x53, 0x45, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x52, 0x49, 0x4f,
-     0x5f, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09,
-     0x50, 0x52, 0x49, 0x4f, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x04, 0x12,
-     0x0d, 0x0a, 0x09, 0x50, 0x52, 0x49, 0x4f, 0x5f, 0x57, 0x41, 0x52, 0x4e,
-     0x10, 0x05, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x52, 0x49, 0x4f, 0x5f, 0x45,
-     0x52, 0x52, 0x4f, 0x52, 0x10, 0x06, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x52,
-     0x49, 0x4f, 0x5f, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x10, 0x07, 0x2a, 0xbf,
-     0x06, 0x0a, 0x0f, 0x4d, 0x65, 0x6d, 0x69, 0x6e, 0x66, 0x6f, 0x43, 0x6f,
-     0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x17, 0x0a, 0x13, 0x4d, 0x45,
-     0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43,
-     0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x4d,
-     0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x4d, 0x45, 0x4d, 0x5f, 0x54,
-     0x4f, 0x54, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x4d, 0x45,
-     0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x4d, 0x45, 0x4d, 0x5f, 0x46, 0x52,
-     0x45, 0x45, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x4d, 0x45, 0x4d, 0x49,
-     0x4e, 0x46, 0x4f, 0x5f, 0x4d, 0x45, 0x4d, 0x5f, 0x41, 0x56, 0x41, 0x49,
-     0x4c, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, 0x4d,
-     0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45,
-     0x52, 0x53, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x45, 0x4d, 0x49,
-     0x4e, 0x46, 0x4f, 0x5f, 0x43, 0x41, 0x43, 0x48, 0x45, 0x44, 0x10, 0x05,
-     0x12, 0x17, 0x0a, 0x13, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f,
-     0x53, 0x57, 0x41, 0x50, 0x5f, 0x43, 0x41, 0x43, 0x48, 0x45, 0x44, 0x10,
-     0x06, 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f,
-     0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x07, 0x12, 0x14, 0x0a,
-     0x10, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x49, 0x4e, 0x41,
-     0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x08, 0x12, 0x17, 0x0a, 0x13, 0x4d,
-     0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56,
-     0x45, 0x5f, 0x41, 0x4e, 0x4f, 0x4e, 0x10, 0x09, 0x12, 0x19, 0x0a, 0x15,
-     0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x49, 0x4e, 0x41, 0x43,
-     0x54, 0x49, 0x56, 0x45, 0x5f, 0x41, 0x4e, 0x4f, 0x4e, 0x10, 0x0a, 0x12,
-     0x17, 0x0a, 0x13, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x41,
-     0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x0b,
-     0x12, 0x19, 0x0a, 0x15, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f,
-     0x49, 0x4e, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x46, 0x49, 0x4c,
-     0x45, 0x10, 0x0c, 0x12, 0x17, 0x0a, 0x13, 0x4d, 0x45, 0x4d, 0x49, 0x4e,
-     0x46, 0x4f, 0x5f, 0x55, 0x4e, 0x45, 0x56, 0x49, 0x43, 0x54, 0x41, 0x42,
-     0x4c, 0x45, 0x10, 0x0d, 0x12, 0x13, 0x0a, 0x0f, 0x4d, 0x45, 0x4d, 0x49,
-     0x4e, 0x46, 0x4f, 0x5f, 0x4d, 0x4c, 0x4f, 0x43, 0x4b, 0x45, 0x44, 0x10,
-     0x0e, 0x12, 0x16, 0x0a, 0x12, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f,
-     0x5f, 0x53, 0x57, 0x41, 0x50, 0x5f, 0x54, 0x4f, 0x54, 0x41, 0x4c, 0x10,
-     0x0f, 0x12, 0x15, 0x0a, 0x11, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f,
-     0x5f, 0x53, 0x57, 0x41, 0x50, 0x5f, 0x46, 0x52, 0x45, 0x45, 0x10, 0x10,
-     0x12, 0x11, 0x0a, 0x0d, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f,
-     0x44, 0x49, 0x52, 0x54, 0x59, 0x10, 0x11, 0x12, 0x15, 0x0a, 0x11, 0x4d,
-     0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45,
-     0x42, 0x41, 0x43, 0x4b, 0x10, 0x12, 0x12, 0x16, 0x0a, 0x12, 0x4d, 0x45,
-     0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x41, 0x4e, 0x4f, 0x4e, 0x5f, 0x50,
-     0x41, 0x47, 0x45, 0x53, 0x10, 0x13, 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x45,
-     0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x4d, 0x41, 0x50, 0x50, 0x45, 0x44,
-     0x10, 0x14, 0x12, 0x11, 0x0a, 0x0d, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46,
-     0x4f, 0x5f, 0x53, 0x48, 0x4d, 0x45, 0x4d, 0x10, 0x15, 0x12, 0x10, 0x0a,
-     0x0c, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x53, 0x4c, 0x41,
-     0x42, 0x10, 0x16, 0x12, 0x1c, 0x0a, 0x18, 0x4d, 0x45, 0x4d, 0x49, 0x4e,
-     0x46, 0x4f, 0x5f, 0x53, 0x4c, 0x41, 0x42, 0x5f, 0x52, 0x45, 0x43, 0x4c,
-     0x41, 0x49, 0x4d, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x17, 0x12, 0x1e, 0x0a,
-     0x1a, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x53, 0x4c, 0x41,
-     0x42, 0x5f, 0x55, 0x4e, 0x52, 0x45, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x41,
-     0x42, 0x4c, 0x45, 0x10, 0x18, 0x12, 0x18, 0x0a, 0x14, 0x4d, 0x45, 0x4d,
-     0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x4b, 0x45, 0x52, 0x4e, 0x45, 0x4c, 0x5f,
-     0x53, 0x54, 0x41, 0x43, 0x4b, 0x10, 0x19, 0x12, 0x17, 0x0a, 0x13, 0x4d,
-     0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x5f,
-     0x54, 0x41, 0x42, 0x4c, 0x45, 0x53, 0x10, 0x1a, 0x12, 0x18, 0x0a, 0x14,
-     0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x43, 0x4f, 0x4d, 0x4d,
-     0x49, 0x54, 0x5f, 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x10, 0x1b, 0x12, 0x17,
-     0x0a, 0x13, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x43, 0x4f,
-     0x4d, 0x4d, 0x49, 0x54, 0x45, 0x44, 0x5f, 0x41, 0x53, 0x10, 0x1c, 0x12,
-     0x19, 0x0a, 0x15, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x56,
-     0x4d, 0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x5f, 0x54, 0x4f, 0x54, 0x41, 0x4c,
-     0x10, 0x1d, 0x12, 0x18, 0x0a, 0x14, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46,
-     0x4f, 0x5f, 0x56, 0x4d, 0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x5f, 0x55, 0x53,
-     0x45, 0x44, 0x10, 0x1e, 0x12, 0x19, 0x0a, 0x15, 0x4d, 0x45, 0x4d, 0x49,
-     0x4e, 0x46, 0x4f, 0x5f, 0x56, 0x4d, 0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x5f,
-     0x43, 0x48, 0x55, 0x4e, 0x4b, 0x10, 0x1f, 0x12, 0x15, 0x0a, 0x11, 0x4d,
-     0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x43, 0x4d, 0x41, 0x5f, 0x54,
-     0x4f, 0x54, 0x41, 0x4c, 0x10, 0x20, 0x12, 0x14, 0x0a, 0x10, 0x4d, 0x45,
-     0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x43, 0x4d, 0x41, 0x5f, 0x46, 0x52,
-     0x45, 0x45, 0x10, 0x21, 0x2a, 0xc6, 0x15, 0x0a, 0x0e, 0x56, 0x6d, 0x73,
-     0x74, 0x61, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12,
-     0x16, 0x0a, 0x12, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x55, 0x4e,
-     0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12,
-     0x18, 0x0a, 0x14, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52,
-     0x5f, 0x46, 0x52, 0x45, 0x45, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x53, 0x10,
-     0x01, 0x12, 0x19, 0x0a, 0x15, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
-     0x4e, 0x52, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x5f, 0x42, 0x41, 0x54,
-     0x43, 0x48, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x56, 0x4d, 0x53, 0x54,
-     0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x49, 0x4e, 0x41, 0x43, 0x54, 0x49,
-     0x56, 0x45, 0x5f, 0x41, 0x4e, 0x4f, 0x4e, 0x10, 0x03, 0x12, 0x19, 0x0a,
-     0x15, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x41,
-     0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x41, 0x4e, 0x4f, 0x4e, 0x10, 0x04,
-     0x12, 0x1b, 0x0a, 0x17, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e,
-     0x52, 0x5f, 0x49, 0x4e, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x46,
-     0x49, 0x4c, 0x45, 0x10, 0x05, 0x12, 0x19, 0x0a, 0x15, 0x56, 0x4d, 0x53,
-     0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56,
-     0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x06, 0x12, 0x19, 0x0a, 0x15,
-     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x55, 0x4e,
-     0x45, 0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x07, 0x12,
-     0x13, 0x0a, 0x0f, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52,
-     0x5f, 0x4d, 0x4c, 0x4f, 0x43, 0x4b, 0x10, 0x08, 0x12, 0x18, 0x0a, 0x14,
-     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x41, 0x4e,
-     0x4f, 0x4e, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x53, 0x10, 0x09, 0x12, 0x14,
-     0x0a, 0x10, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f,
-     0x4d, 0x41, 0x50, 0x50, 0x45, 0x44, 0x10, 0x0a, 0x12, 0x18, 0x0a, 0x14,
-     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x46, 0x49,
-     0x4c, 0x45, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x53, 0x10, 0x0b, 0x12, 0x13,
-     0x0a, 0x0f, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f,
-     0x44, 0x49, 0x52, 0x54, 0x59, 0x10, 0x0c, 0x12, 0x17, 0x0a, 0x13, 0x56,
-     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x57, 0x52, 0x49,
-     0x54, 0x45, 0x42, 0x41, 0x43, 0x4b, 0x10, 0x0d, 0x12, 0x1e, 0x0a, 0x1a,
-     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x53, 0x4c,
+     0x72, 0x12, 0x3b, 0x0a, 0x1a, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65,
+     0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x72, 0x65, 0x67, 0x65, 0x78,
+     0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28,
+     0x09, 0x52, 0x17, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x72, 0x4e,
+     0x61, 0x6d, 0x65, 0x52, 0x65, 0x67, 0x65, 0x78, 0x46, 0x69, 0x6c, 0x74,
+     0x65, 0x72, 0x1a, 0xe9, 0x01, 0x0a, 0x11, 0x42, 0x75, 0x69, 0x6c, 0x74,
+     0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65,
+     0x12, 0x3c, 0x0a, 0x1a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f,
+     0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68,
+     0x6f, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
+     0x52, 0x18, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6c, 0x6f,
+     0x63, 0x6b, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x74, 0x69,
+     0x6e, 0x67, 0x12, 0x30, 0x0a, 0x14, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c,
+     0x65, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66,
+     0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x64, 0x69,
+     0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43, 0x6f,
+     0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61,
+     0x62, 0x6c, 0x65, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x69,
+     0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64,
+     0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d,
+     0x49, 0x6e, 0x66, 0x6f, 0x12, 0x34, 0x0a, 0x16, 0x64, 0x69, 0x73, 0x61,
+     0x62, 0x6c, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f,
+     0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08,
+     0x52, 0x14, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x72,
+     0x76, 0x69, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x77,
+     0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x72, 0x43, 0x6f,
+     0x6e, 0x66, 0x69, 0x67, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x64,
+     0x75, 0x63, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20,
+     0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65,
+     0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0b, 0x73, 0x68, 0x6d,
+     0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6b, 0x62, 0x18, 0x02, 0x20, 0x01,
+     0x28, 0x0d, 0x52, 0x09, 0x73, 0x68, 0x6d, 0x53, 0x69, 0x7a, 0x65, 0x4b,
+     0x62, 0x12, 0x20, 0x0a, 0x0c, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69,
+     0x7a, 0x65, 0x5f, 0x6b, 0x62, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52,
+     0x0a, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x4b, 0x62, 0x1a,
+     0xe4, 0x01, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x64, 0x4d, 0x65,
+     0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2e, 0x0a, 0x13, 0x74, 0x72,
+     0x69, 0x67, 0x67, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6c, 0x65,
+     0x72, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52,
+     0x11, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x41,
+     0x6c, 0x65, 0x72, 0x74, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x74, 0x72,
+     0x69, 0x67, 0x67, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6e,
+     0x66, 0x69, 0x67, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
+     0x05, 0x52, 0x13, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x69, 0x6e,
+     0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x55, 0x69, 0x64, 0x12, 0x30,
+     0x0a, 0x14, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x69, 0x6e, 0x67,
+     0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x03,
+     0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65,
+     0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x49, 0x64,
+     0x12, 0x3c, 0x0a, 0x1a, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x69,
+     0x6e, 0x67, 0x5f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+     0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03,
+     0x52, 0x18, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x69, 0x6e, 0x67,
+     0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+     0x49, 0x64, 0x1a, 0x4c, 0x0a, 0x12, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72,
+     0x61, 0x69, 0x6c, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x73,
+     0x12, 0x36, 0x0a, 0x18, 0x6d, 0x61, 0x78, 0x5f, 0x75, 0x70, 0x6c, 0x6f,
+     0x61, 0x64, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x64, 0x61, 0x79, 0x5f, 0x62,
+     0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x14,
+     0x6d, 0x61, 0x78, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x65, 0x72,
+     0x44, 0x61, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x1a, 0xa0, 0x03, 0x0a,
+     0x0d, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66,
+     0x69, 0x67, 0x12, 0x59, 0x0a, 0x0c, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65,
+     0x72, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e,
+     0x32, 0x36, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65,
+     0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x54, 0x72, 0x69, 0x67, 0x67,
+     0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x54, 0x72, 0x69,
+     0x67, 0x67, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x0b, 0x74, 0x72,
+     0x69, 0x67, 0x67, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x4e, 0x0a,
+     0x08, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20,
+     0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72,
+     0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x54, 0x72,
+     0x69, 0x67, 0x67, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
+     0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x52, 0x08, 0x74, 0x72, 0x69,
+     0x67, 0x67, 0x65, 0x72, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x74, 0x72, 0x69,
+     0x67, 0x67, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74,
+     0x5f, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x74,
+     0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75,
+     0x74, 0x4d, 0x73, 0x1a, 0x71, 0x0a, 0x07, 0x54, 0x72, 0x69, 0x67, 0x67,
+     0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
+     0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2e,
+     0x0a, 0x13, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x72, 0x5f, 0x6e,
+     0x61, 0x6d, 0x65, 0x5f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x02, 0x20,
+     0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65,
+     0x72, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x67, 0x65, 0x78, 0x12, 0x22,
+     0x0a, 0x0d, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79,
+     0x5f, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73,
+     0x74, 0x6f, 0x70, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x73, 0x22, 0x43,
+     0x0a, 0x0b, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x4d, 0x6f, 0x64,
+     0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49,
+     0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54,
+     0x41, 0x52, 0x54, 0x5f, 0x54, 0x52, 0x41, 0x43, 0x49, 0x4e, 0x47, 0x10,
+     0x01, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x54, 0x4f, 0x50, 0x5f, 0x54, 0x52,
+     0x41, 0x43, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x1a, 0x40, 0x0a, 0x16, 0x49,
+     0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x53, 0x74,
+     0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x26, 0x0a,
+     0x0f, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f,
+     0x64, 0x5f, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d,
+     0x63, 0x6c, 0x65, 0x61, 0x72, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x4d,
+     0x73, 0x1a, 0xbc, 0x01, 0x0a, 0x14, 0x49, 0x6e, 0x63, 0x69, 0x64, 0x65,
+     0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66,
+     0x69, 0x67, 0x12, 0x2f, 0x0a, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e,
+     0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67,
+     0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x64, 0x65, 0x73,
+     0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x63, 0x6b,
+     0x61, 0x67, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x64, 0x65, 0x73, 0x74, 0x69,
+     0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73,
+     0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x64, 0x65, 0x73, 0x74,
+     0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x61, 0x73, 0x73,
+     0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x76, 0x61, 0x63, 0x79, 0x5f,
+     0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52,
+     0x0c, 0x70, 0x72, 0x69, 0x76, 0x61, 0x63, 0x79, 0x4c, 0x65, 0x76, 0x65,
+     0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x64, 0x72,
+     0x6f, 0x70, 0x62, 0x6f, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52,
+     0x0b, 0x73, 0x6b, 0x69, 0x70, 0x44, 0x72, 0x6f, 0x70, 0x62, 0x6f, 0x78,
+     0x22, 0x55, 0x0a, 0x15, 0x4c, 0x6f, 0x63, 0x6b, 0x64, 0x6f, 0x77, 0x6e,
+     0x4d, 0x6f, 0x64, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f,
+     0x6e, 0x12, 0x16, 0x0a, 0x12, 0x4c, 0x4f, 0x43, 0x4b, 0x44, 0x4f, 0x57,
+     0x4e, 0x5f, 0x55, 0x4e, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x44, 0x10,
+     0x00, 0x12, 0x12, 0x0a, 0x0e, 0x4c, 0x4f, 0x43, 0x4b, 0x44, 0x4f, 0x57,
+     0x4e, 0x5f, 0x43, 0x4c, 0x45, 0x41, 0x52, 0x10, 0x01, 0x12, 0x10, 0x0a,
+     0x0c, 0x4c, 0x4f, 0x43, 0x4b, 0x44, 0x4f, 0x57, 0x4e, 0x5f, 0x53, 0x45,
+     0x54, 0x10, 0x02, 0x22, 0x51, 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x70, 0x72,
+     0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20,
+     0x0a, 0x1c, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f,
+     0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45,
+     0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18,
+     0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f,
+     0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, 0x46, 0x4c, 0x41, 0x54, 0x45,
+     0x10, 0x01, 0x4a, 0x04, 0x08, 0x0f, 0x10, 0x10, 0x4a, 0x04, 0x08, 0x1a,
+     0x10, 0x1b, 0x2a, 0x8e, 0x01, 0x0a, 0x0c, 0x41, 0x6e, 0x64, 0x72, 0x6f,
+     0x69, 0x64, 0x4c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x0f, 0x0a, 0x0b, 0x4c,
+     0x49, 0x44, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00,
+     0x12, 0x0d, 0x0a, 0x09, 0x4c, 0x49, 0x44, 0x5f, 0x52, 0x41, 0x44, 0x49,
+     0x4f, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x4c, 0x49, 0x44, 0x5f, 0x45,
+     0x56, 0x45, 0x4e, 0x54, 0x53, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x4c,
+     0x49, 0x44, 0x5f, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x10, 0x03, 0x12,
+     0x0d, 0x0a, 0x09, 0x4c, 0x49, 0x44, 0x5f, 0x43, 0x52, 0x41, 0x53, 0x48,
+     0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x4c, 0x49, 0x44, 0x5f, 0x53, 0x54,
+     0x41, 0x54, 0x53, 0x10, 0x05, 0x12, 0x10, 0x0a, 0x0c, 0x4c, 0x49, 0x44,
+     0x5f, 0x53, 0x45, 0x43, 0x55, 0x52, 0x49, 0x54, 0x59, 0x10, 0x06, 0x12,
+     0x0e, 0x0a, 0x0a, 0x4c, 0x49, 0x44, 0x5f, 0x4b, 0x45, 0x52, 0x4e, 0x45,
+     0x4c, 0x10, 0x07, 0x2a, 0x9b, 0x01, 0x0a, 0x12, 0x41, 0x6e, 0x64, 0x72,
+     0x6f, 0x69, 0x64, 0x4c, 0x6f, 0x67, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69,
+     0x74, 0x79, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x52, 0x49, 0x4f, 0x5f, 0x55,
+     0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00,
+     0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x52, 0x49, 0x4f, 0x5f, 0x55, 0x4e, 0x55,
+     0x53, 0x45, 0x44, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x52, 0x49,
+     0x4f, 0x5f, 0x56, 0x45, 0x52, 0x42, 0x4f, 0x53, 0x45, 0x10, 0x02, 0x12,
+     0x0e, 0x0a, 0x0a, 0x50, 0x52, 0x49, 0x4f, 0x5f, 0x44, 0x45, 0x42, 0x55,
+     0x47, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x52, 0x49, 0x4f, 0x5f,
+     0x49, 0x4e, 0x46, 0x4f, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x52,
+     0x49, 0x4f, 0x5f, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x05, 0x12, 0x0e, 0x0a,
+     0x0a, 0x50, 0x52, 0x49, 0x4f, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10,
+     0x06, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x52, 0x49, 0x4f, 0x5f, 0x46, 0x41,
+     0x54, 0x41, 0x4c, 0x10, 0x07, 0x2a, 0xbf, 0x06, 0x0a, 0x0f, 0x4d, 0x65,
+     0x6d, 0x69, 0x6e, 0x66, 0x6f, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72,
+     0x73, 0x12, 0x17, 0x0a, 0x13, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f,
+     0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44,
+     0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46,
+     0x4f, 0x5f, 0x4d, 0x45, 0x4d, 0x5f, 0x54, 0x4f, 0x54, 0x41, 0x4c, 0x10,
+     0x01, 0x12, 0x14, 0x0a, 0x10, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f,
+     0x5f, 0x4d, 0x45, 0x4d, 0x5f, 0x46, 0x52, 0x45, 0x45, 0x10, 0x02, 0x12,
+     0x19, 0x0a, 0x15, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x4d,
+     0x45, 0x4d, 0x5f, 0x41, 0x56, 0x41, 0x49, 0x4c, 0x41, 0x42, 0x4c, 0x45,
+     0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46,
+     0x4f, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x53, 0x10, 0x04, 0x12,
+     0x12, 0x0a, 0x0e, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x43,
+     0x41, 0x43, 0x48, 0x45, 0x44, 0x10, 0x05, 0x12, 0x17, 0x0a, 0x13, 0x4d,
+     0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x53, 0x57, 0x41, 0x50, 0x5f,
+     0x43, 0x41, 0x43, 0x48, 0x45, 0x44, 0x10, 0x06, 0x12, 0x12, 0x0a, 0x0e,
+     0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x41, 0x43, 0x54, 0x49,
+     0x56, 0x45, 0x10, 0x07, 0x12, 0x14, 0x0a, 0x10, 0x4d, 0x45, 0x4d, 0x49,
+     0x4e, 0x46, 0x4f, 0x5f, 0x49, 0x4e, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45,
+     0x10, 0x08, 0x12, 0x17, 0x0a, 0x13, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46,
+     0x4f, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x41, 0x4e, 0x4f,
+     0x4e, 0x10, 0x09, 0x12, 0x19, 0x0a, 0x15, 0x4d, 0x45, 0x4d, 0x49, 0x4e,
+     0x46, 0x4f, 0x5f, 0x49, 0x4e, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f,
+     0x41, 0x4e, 0x4f, 0x4e, 0x10, 0x0a, 0x12, 0x17, 0x0a, 0x13, 0x4d, 0x45,
+     0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45,
+     0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x0b, 0x12, 0x19, 0x0a, 0x15, 0x4d,
+     0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x49, 0x4e, 0x41, 0x43, 0x54,
+     0x49, 0x56, 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x0c, 0x12, 0x17,
+     0x0a, 0x13, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x55, 0x4e,
+     0x45, 0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x0d, 0x12,
+     0x13, 0x0a, 0x0f, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x4d,
+     0x4c, 0x4f, 0x43, 0x4b, 0x45, 0x44, 0x10, 0x0e, 0x12, 0x16, 0x0a, 0x12,
+     0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x53, 0x57, 0x41, 0x50,
+     0x5f, 0x54, 0x4f, 0x54, 0x41, 0x4c, 0x10, 0x0f, 0x12, 0x15, 0x0a, 0x11,
+     0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x53, 0x57, 0x41, 0x50,
+     0x5f, 0x46, 0x52, 0x45, 0x45, 0x10, 0x10, 0x12, 0x11, 0x0a, 0x0d, 0x4d,
+     0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x44, 0x49, 0x52, 0x54, 0x59,
+     0x10, 0x11, 0x12, 0x15, 0x0a, 0x11, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46,
+     0x4f, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x42, 0x41, 0x43, 0x4b, 0x10,
+     0x12, 0x12, 0x16, 0x0a, 0x12, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f,
+     0x5f, 0x41, 0x4e, 0x4f, 0x4e, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x53, 0x10,
+     0x13, 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f,
+     0x5f, 0x4d, 0x41, 0x50, 0x50, 0x45, 0x44, 0x10, 0x14, 0x12, 0x11, 0x0a,
+     0x0d, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x53, 0x48, 0x4d,
+     0x45, 0x4d, 0x10, 0x15, 0x12, 0x10, 0x0a, 0x0c, 0x4d, 0x45, 0x4d, 0x49,
+     0x4e, 0x46, 0x4f, 0x5f, 0x53, 0x4c, 0x41, 0x42, 0x10, 0x16, 0x12, 0x1c,
+     0x0a, 0x18, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x53, 0x4c,
      0x41, 0x42, 0x5f, 0x52, 0x45, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x41, 0x42,
-     0x4c, 0x45, 0x10, 0x0e, 0x12, 0x20, 0x0a, 0x1c, 0x56, 0x4d, 0x53, 0x54,
-     0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x53, 0x4c, 0x41, 0x42, 0x5f, 0x55,
-     0x4e, 0x52, 0x45, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x41, 0x42, 0x4c, 0x45,
-     0x10, 0x0f, 0x12, 0x1e, 0x0a, 0x1a, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
-     0x5f, 0x4e, 0x52, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x41, 0x42,
-     0x4c, 0x45, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x53, 0x10, 0x10, 0x12, 0x1a,
-     0x0a, 0x16, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f,
+     0x4c, 0x45, 0x10, 0x17, 0x12, 0x1e, 0x0a, 0x1a, 0x4d, 0x45, 0x4d, 0x49,
+     0x4e, 0x46, 0x4f, 0x5f, 0x53, 0x4c, 0x41, 0x42, 0x5f, 0x55, 0x4e, 0x52,
+     0x45, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x18,
+     0x12, 0x18, 0x0a, 0x14, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f,
      0x4b, 0x45, 0x52, 0x4e, 0x45, 0x4c, 0x5f, 0x53, 0x54, 0x41, 0x43, 0x4b,
-     0x10, 0x11, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
-     0x5f, 0x4e, 0x52, 0x5f, 0x4f, 0x56, 0x45, 0x52, 0x48, 0x45, 0x41, 0x44,
-     0x10, 0x12, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
-     0x5f, 0x4e, 0x52, 0x5f, 0x55, 0x4e, 0x53, 0x54, 0x41, 0x42, 0x4c, 0x45,
-     0x10, 0x13, 0x12, 0x14, 0x0a, 0x10, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
-     0x5f, 0x4e, 0x52, 0x5f, 0x42, 0x4f, 0x55, 0x4e, 0x43, 0x45, 0x10, 0x14,
-     0x12, 0x1a, 0x0a, 0x16, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e,
-     0x52, 0x5f, 0x56, 0x4d, 0x53, 0x43, 0x41, 0x4e, 0x5f, 0x57, 0x52, 0x49,
-     0x54, 0x45, 0x10, 0x15, 0x12, 0x26, 0x0a, 0x22, 0x56, 0x4d, 0x53, 0x54,
-     0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x56, 0x4d, 0x53, 0x43, 0x41, 0x4e,
-     0x5f, 0x49, 0x4d, 0x4d, 0x45, 0x44, 0x49, 0x41, 0x54, 0x45, 0x5f, 0x52,
-     0x45, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x10, 0x16, 0x12, 0x1c, 0x0a, 0x18,
-     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x57, 0x52,
-     0x49, 0x54, 0x45, 0x42, 0x41, 0x43, 0x4b, 0x5f, 0x54, 0x45, 0x4d, 0x50,
-     0x10, 0x17, 0x12, 0x1b, 0x0a, 0x17, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
+     0x10, 0x19, 0x12, 0x17, 0x0a, 0x13, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46,
+     0x4f, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x41, 0x42, 0x4c, 0x45,
+     0x53, 0x10, 0x1a, 0x12, 0x18, 0x0a, 0x14, 0x4d, 0x45, 0x4d, 0x49, 0x4e,
+     0x46, 0x4f, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x5f, 0x4c, 0x49,
+     0x4d, 0x49, 0x54, 0x10, 0x1b, 0x12, 0x17, 0x0a, 0x13, 0x4d, 0x45, 0x4d,
+     0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x45,
+     0x44, 0x5f, 0x41, 0x53, 0x10, 0x1c, 0x12, 0x19, 0x0a, 0x15, 0x4d, 0x45,
+     0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x56, 0x4d, 0x41, 0x4c, 0x4c, 0x4f,
+     0x43, 0x5f, 0x54, 0x4f, 0x54, 0x41, 0x4c, 0x10, 0x1d, 0x12, 0x18, 0x0a,
+     0x14, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x56, 0x4d, 0x41,
+     0x4c, 0x4c, 0x4f, 0x43, 0x5f, 0x55, 0x53, 0x45, 0x44, 0x10, 0x1e, 0x12,
+     0x19, 0x0a, 0x15, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x56,
+     0x4d, 0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x5f, 0x43, 0x48, 0x55, 0x4e, 0x4b,
+     0x10, 0x1f, 0x12, 0x15, 0x0a, 0x11, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46,
+     0x4f, 0x5f, 0x43, 0x4d, 0x41, 0x5f, 0x54, 0x4f, 0x54, 0x41, 0x4c, 0x10,
+     0x20, 0x12, 0x14, 0x0a, 0x10, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f,
+     0x5f, 0x43, 0x4d, 0x41, 0x5f, 0x46, 0x52, 0x45, 0x45, 0x10, 0x21, 0x2a,
+     0xc6, 0x15, 0x0a, 0x0e, 0x56, 0x6d, 0x73, 0x74, 0x61, 0x74, 0x43, 0x6f,
+     0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x4d,
+     0x53, 0x54, 0x41, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49,
+     0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x56, 0x4d,
+     0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x46, 0x52, 0x45, 0x45,
+     0x5f, 0x50, 0x41, 0x47, 0x45, 0x53, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15,
+     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x41, 0x4c,
+     0x4c, 0x4f, 0x43, 0x5f, 0x42, 0x41, 0x54, 0x43, 0x48, 0x10, 0x02, 0x12,
+     0x1b, 0x0a, 0x17, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52,
+     0x5f, 0x49, 0x4e, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x41, 0x4e,
+     0x4f, 0x4e, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x56, 0x4d, 0x53, 0x54,
+     0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45,
+     0x5f, 0x41, 0x4e, 0x4f, 0x4e, 0x10, 0x04, 0x12, 0x1b, 0x0a, 0x17, 0x56,
+     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x49, 0x4e, 0x41,
+     0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x05,
+     0x12, 0x19, 0x0a, 0x15, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e,
+     0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x46, 0x49, 0x4c,
+     0x45, 0x10, 0x06, 0x12, 0x19, 0x0a, 0x15, 0x56, 0x4d, 0x53, 0x54, 0x41,
+     0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x55, 0x4e, 0x45, 0x56, 0x49, 0x43, 0x54,
+     0x41, 0x42, 0x4c, 0x45, 0x10, 0x07, 0x12, 0x13, 0x0a, 0x0f, 0x56, 0x4d,
+     0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x4d, 0x4c, 0x4f, 0x43,
+     0x4b, 0x10, 0x08, 0x12, 0x18, 0x0a, 0x14, 0x56, 0x4d, 0x53, 0x54, 0x41,
+     0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x41, 0x4e, 0x4f, 0x4e, 0x5f, 0x50, 0x41,
+     0x47, 0x45, 0x53, 0x10, 0x09, 0x12, 0x14, 0x0a, 0x10, 0x56, 0x4d, 0x53,
+     0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x4d, 0x41, 0x50, 0x50, 0x45,
+     0x44, 0x10, 0x0a, 0x12, 0x18, 0x0a, 0x14, 0x56, 0x4d, 0x53, 0x54, 0x41,
+     0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x50, 0x41,
+     0x47, 0x45, 0x53, 0x10, 0x0b, 0x12, 0x13, 0x0a, 0x0f, 0x56, 0x4d, 0x53,
+     0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x44, 0x49, 0x52, 0x54, 0x59,
+     0x10, 0x0c, 0x12, 0x17, 0x0a, 0x13, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
+     0x5f, 0x4e, 0x52, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x42, 0x41, 0x43,
+     0x4b, 0x10, 0x0d, 0x12, 0x1e, 0x0a, 0x1a, 0x56, 0x4d, 0x53, 0x54, 0x41,
+     0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x53, 0x4c, 0x41, 0x42, 0x5f, 0x52, 0x45,
+     0x43, 0x4c, 0x41, 0x49, 0x4d, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x0e, 0x12,
+     0x20, 0x0a, 0x1c, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52,
+     0x5f, 0x53, 0x4c, 0x41, 0x42, 0x5f, 0x55, 0x4e, 0x52, 0x45, 0x43, 0x4c,
+     0x41, 0x49, 0x4d, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x0f, 0x12, 0x1e, 0x0a,
+     0x1a, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x50,
+     0x41, 0x47, 0x45, 0x5f, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x50, 0x41,
+     0x47, 0x45, 0x53, 0x10, 0x10, 0x12, 0x1a, 0x0a, 0x16, 0x56, 0x4d, 0x53,
+     0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x4b, 0x45, 0x52, 0x4e, 0x45,
+     0x4c, 0x5f, 0x53, 0x54, 0x41, 0x43, 0x4b, 0x10, 0x11, 0x12, 0x16, 0x0a,
+     0x12, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x4f,
+     0x56, 0x45, 0x52, 0x48, 0x45, 0x41, 0x44, 0x10, 0x12, 0x12, 0x16, 0x0a,
+     0x12, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x55,
+     0x4e, 0x53, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x13, 0x12, 0x14, 0x0a,
+     0x10, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x42,
+     0x4f, 0x55, 0x4e, 0x43, 0x45, 0x10, 0x14, 0x12, 0x1a, 0x0a, 0x16, 0x56,
+     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x56, 0x4d, 0x53,
+     0x43, 0x41, 0x4e, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x10, 0x15, 0x12,
+     0x26, 0x0a, 0x22, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52,
+     0x5f, 0x56, 0x4d, 0x53, 0x43, 0x41, 0x4e, 0x5f, 0x49, 0x4d, 0x4d, 0x45,
+     0x44, 0x49, 0x41, 0x54, 0x45, 0x5f, 0x52, 0x45, 0x43, 0x4c, 0x41, 0x49,
+     0x4d, 0x10, 0x16, 0x12, 0x1c, 0x0a, 0x18, 0x56, 0x4d, 0x53, 0x54, 0x41,
+     0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x42, 0x41,
+     0x43, 0x4b, 0x5f, 0x54, 0x45, 0x4d, 0x50, 0x10, 0x17, 0x12, 0x1b, 0x0a,
+     0x17, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x49,
+     0x53, 0x4f, 0x4c, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x41, 0x4e, 0x4f, 0x4e,
+     0x10, 0x18, 0x12, 0x1b, 0x0a, 0x17, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
      0x5f, 0x4e, 0x52, 0x5f, 0x49, 0x53, 0x4f, 0x4c, 0x41, 0x54, 0x45, 0x44,
-     0x5f, 0x41, 0x4e, 0x4f, 0x4e, 0x10, 0x18, 0x12, 0x1b, 0x0a, 0x17, 0x56,
-     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x49, 0x53, 0x4f,
-     0x4c, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x19,
-     0x12, 0x13, 0x0a, 0x0f, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e,
-     0x52, 0x5f, 0x53, 0x48, 0x4d, 0x45, 0x4d, 0x10, 0x1a, 0x12, 0x15, 0x0a,
-     0x11, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x44,
-     0x49, 0x52, 0x54, 0x49, 0x45, 0x44, 0x10, 0x1b, 0x12, 0x15, 0x0a, 0x11,
-     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x57, 0x52,
-     0x49, 0x54, 0x54, 0x45, 0x4e, 0x10, 0x1c, 0x12, 0x1b, 0x0a, 0x17, 0x56,
-     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x50, 0x41, 0x47,
-     0x45, 0x53, 0x5f, 0x53, 0x43, 0x41, 0x4e, 0x4e, 0x45, 0x44, 0x10, 0x1d,
-     0x12, 0x1d, 0x0a, 0x19, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x57,
-     0x4f, 0x52, 0x4b, 0x49, 0x4e, 0x47, 0x53, 0x45, 0x54, 0x5f, 0x52, 0x45,
-     0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x1e, 0x12, 0x1e, 0x0a, 0x1a, 0x56,
+     0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x19, 0x12, 0x13, 0x0a, 0x0f, 0x56,
+     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x53, 0x48, 0x4d,
+     0x45, 0x4d, 0x10, 0x1a, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x4d, 0x53, 0x54,
+     0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x44, 0x49, 0x52, 0x54, 0x49, 0x45,
+     0x44, 0x10, 0x1b, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x4d, 0x53, 0x54, 0x41,
+     0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x54, 0x45, 0x4e,
+     0x10, 0x1c, 0x12, 0x1b, 0x0a, 0x17, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
+     0x5f, 0x4e, 0x52, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x53, 0x5f, 0x53, 0x43,
+     0x41, 0x4e, 0x4e, 0x45, 0x44, 0x10, 0x1d, 0x12, 0x1d, 0x0a, 0x19, 0x56,
      0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x49, 0x4e,
-     0x47, 0x53, 0x45, 0x54, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x41, 0x54,
-     0x45, 0x10, 0x1f, 0x12, 0x21, 0x0a, 0x1d, 0x56, 0x4d, 0x53, 0x54, 0x41,
-     0x54, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x49, 0x4e, 0x47, 0x53, 0x45, 0x54,
-     0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x52, 0x45, 0x43, 0x4c, 0x41, 0x49, 0x4d,
-     0x10, 0x20, 0x12, 0x28, 0x0a, 0x24, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
-     0x5f, 0x4e, 0x52, 0x5f, 0x41, 0x4e, 0x4f, 0x4e, 0x5f, 0x54, 0x52, 0x41,
-     0x4e, 0x53, 0x50, 0x41, 0x52, 0x45, 0x4e, 0x54, 0x5f, 0x48, 0x55, 0x47,
-     0x45, 0x50, 0x41, 0x47, 0x45, 0x53, 0x10, 0x21, 0x12, 0x16, 0x0a, 0x12,
-     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x46, 0x52,
-     0x45, 0x45, 0x5f, 0x43, 0x4d, 0x41, 0x10, 0x22, 0x12, 0x17, 0x0a, 0x13,
-     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x53, 0x57,
-     0x41, 0x50, 0x43, 0x41, 0x43, 0x48, 0x45, 0x10, 0x23, 0x12, 0x1d, 0x0a,
-     0x19, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x44,
-     0x49, 0x52, 0x54, 0x59, 0x5f, 0x54, 0x48, 0x52, 0x45, 0x53, 0x48, 0x4f,
-     0x4c, 0x44, 0x10, 0x24, 0x12, 0x28, 0x0a, 0x24, 0x56, 0x4d, 0x53, 0x54,
+     0x47, 0x53, 0x45, 0x54, 0x5f, 0x52, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54,
+     0x10, 0x1e, 0x12, 0x1e, 0x0a, 0x1a, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
+     0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x49, 0x4e, 0x47, 0x53, 0x45, 0x54, 0x5f,
+     0x41, 0x43, 0x54, 0x49, 0x56, 0x41, 0x54, 0x45, 0x10, 0x1f, 0x12, 0x21,
+     0x0a, 0x1d, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x57, 0x4f, 0x52,
+     0x4b, 0x49, 0x4e, 0x47, 0x53, 0x45, 0x54, 0x5f, 0x4e, 0x4f, 0x44, 0x45,
+     0x52, 0x45, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x10, 0x20, 0x12, 0x28, 0x0a,
+     0x24, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x41,
+     0x4e, 0x4f, 0x4e, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x50, 0x41, 0x52,
+     0x45, 0x4e, 0x54, 0x5f, 0x48, 0x55, 0x47, 0x45, 0x50, 0x41, 0x47, 0x45,
+     0x53, 0x10, 0x21, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x4d, 0x53, 0x54, 0x41,
+     0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x46, 0x52, 0x45, 0x45, 0x5f, 0x43, 0x4d,
+     0x41, 0x10, 0x22, 0x12, 0x17, 0x0a, 0x13, 0x56, 0x4d, 0x53, 0x54, 0x41,
+     0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x53, 0x57, 0x41, 0x50, 0x43, 0x41, 0x43,
+     0x48, 0x45, 0x10, 0x23, 0x12, 0x1d, 0x0a, 0x19, 0x56, 0x4d, 0x53, 0x54,
      0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x44, 0x49, 0x52, 0x54, 0x59, 0x5f,
-     0x42, 0x41, 0x43, 0x4b, 0x47, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x54,
-     0x48, 0x52, 0x45, 0x53, 0x48, 0x4f, 0x4c, 0x44, 0x10, 0x25, 0x12, 0x11,
-     0x0a, 0x0d, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x50,
-     0x47, 0x49, 0x4e, 0x10, 0x26, 0x12, 0x12, 0x0a, 0x0e, 0x56, 0x4d, 0x53,
-     0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x50, 0x47, 0x4f, 0x55, 0x54, 0x10,
-     0x27, 0x12, 0x17, 0x0a, 0x13, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
-     0x50, 0x47, 0x50, 0x47, 0x4f, 0x55, 0x54, 0x43, 0x4c, 0x45, 0x41, 0x4e,
-     0x10, 0x28, 0x12, 0x11, 0x0a, 0x0d, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
-     0x5f, 0x50, 0x53, 0x57, 0x50, 0x49, 0x4e, 0x10, 0x29, 0x12, 0x12, 0x0a,
-     0x0e, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x53, 0x57, 0x50,
-     0x4f, 0x55, 0x54, 0x10, 0x2a, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x4d, 0x53,
-     0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x5f,
-     0x44, 0x4d, 0x41, 0x10, 0x2b, 0x12, 0x19, 0x0a, 0x15, 0x56, 0x4d, 0x53,
-     0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x5f,
-     0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x2c, 0x12, 0x1a, 0x0a, 0x16,
-     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x41, 0x4c, 0x4c,
-     0x4f, 0x43, 0x5f, 0x4d, 0x4f, 0x56, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x2d,
-     0x12, 0x11, 0x0a, 0x0d, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50,
-     0x47, 0x46, 0x52, 0x45, 0x45, 0x10, 0x2e, 0x12, 0x15, 0x0a, 0x11, 0x56,
-     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x41, 0x43, 0x54, 0x49,
-     0x56, 0x41, 0x54, 0x45, 0x10, 0x2f, 0x12, 0x17, 0x0a, 0x13, 0x56, 0x4d,
-     0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x44, 0x45, 0x41, 0x43, 0x54,
-     0x49, 0x56, 0x41, 0x54, 0x45, 0x10, 0x30, 0x12, 0x12, 0x0a, 0x0e, 0x56,
-     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x46, 0x41, 0x55, 0x4c,
-     0x54, 0x10, 0x31, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x4d, 0x53, 0x54, 0x41,
-     0x54, 0x5f, 0x50, 0x47, 0x4d, 0x41, 0x4a, 0x46, 0x41, 0x55, 0x4c, 0x54,
-     0x10, 0x32, 0x12, 0x17, 0x0a, 0x13, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
-     0x5f, 0x50, 0x47, 0x52, 0x45, 0x46, 0x49, 0x4c, 0x4c, 0x5f, 0x44, 0x4d,
-     0x41, 0x10, 0x33, 0x12, 0x1a, 0x0a, 0x16, 0x56, 0x4d, 0x53, 0x54, 0x41,
-     0x54, 0x5f, 0x50, 0x47, 0x52, 0x45, 0x46, 0x49, 0x4c, 0x4c, 0x5f, 0x4e,
-     0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x34, 0x12, 0x1b, 0x0a, 0x17, 0x56,
-     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x52, 0x45, 0x46, 0x49,
-     0x4c, 0x4c, 0x5f, 0x4d, 0x4f, 0x56, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x35,
-     0x12, 0x1d, 0x0a, 0x19, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50,
-     0x47, 0x53, 0x54, 0x45, 0x41, 0x4c, 0x5f, 0x4b, 0x53, 0x57, 0x41, 0x50,
-     0x44, 0x5f, 0x44, 0x4d, 0x41, 0x10, 0x36, 0x12, 0x20, 0x0a, 0x1c, 0x56,
+     0x54, 0x48, 0x52, 0x45, 0x53, 0x48, 0x4f, 0x4c, 0x44, 0x10, 0x24, 0x12,
+     0x28, 0x0a, 0x24, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52,
+     0x5f, 0x44, 0x49, 0x52, 0x54, 0x59, 0x5f, 0x42, 0x41, 0x43, 0x4b, 0x47,
+     0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x54, 0x48, 0x52, 0x45, 0x53, 0x48,
+     0x4f, 0x4c, 0x44, 0x10, 0x25, 0x12, 0x11, 0x0a, 0x0d, 0x56, 0x4d, 0x53,
+     0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x50, 0x47, 0x49, 0x4e, 0x10, 0x26,
+     0x12, 0x12, 0x0a, 0x0e, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50,
+     0x47, 0x50, 0x47, 0x4f, 0x55, 0x54, 0x10, 0x27, 0x12, 0x17, 0x0a, 0x13,
+     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x50, 0x47, 0x4f,
+     0x55, 0x54, 0x43, 0x4c, 0x45, 0x41, 0x4e, 0x10, 0x28, 0x12, 0x11, 0x0a,
+     0x0d, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x53, 0x57, 0x50,
+     0x49, 0x4e, 0x10, 0x29, 0x12, 0x12, 0x0a, 0x0e, 0x56, 0x4d, 0x53, 0x54,
+     0x41, 0x54, 0x5f, 0x50, 0x53, 0x57, 0x50, 0x4f, 0x55, 0x54, 0x10, 0x2a,
+     0x12, 0x16, 0x0a, 0x12, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50,
+     0x47, 0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x5f, 0x44, 0x4d, 0x41, 0x10, 0x2b,
+     0x12, 0x19, 0x0a, 0x15, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50,
+     0x47, 0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41,
+     0x4c, 0x10, 0x2c, 0x12, 0x1a, 0x0a, 0x16, 0x56, 0x4d, 0x53, 0x54, 0x41,
+     0x54, 0x5f, 0x50, 0x47, 0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x5f, 0x4d, 0x4f,
+     0x56, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x2d, 0x12, 0x11, 0x0a, 0x0d, 0x56,
+     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x46, 0x52, 0x45, 0x45,
+     0x10, 0x2e, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
+     0x5f, 0x50, 0x47, 0x41, 0x43, 0x54, 0x49, 0x56, 0x41, 0x54, 0x45, 0x10,
+     0x2f, 0x12, 0x17, 0x0a, 0x13, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
+     0x50, 0x47, 0x44, 0x45, 0x41, 0x43, 0x54, 0x49, 0x56, 0x41, 0x54, 0x45,
+     0x10, 0x30, 0x12, 0x12, 0x0a, 0x0e, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
+     0x5f, 0x50, 0x47, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x31, 0x12, 0x15,
+     0x0a, 0x11, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x4d,
+     0x41, 0x4a, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x32, 0x12, 0x17, 0x0a,
+     0x13, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x52, 0x45,
+     0x46, 0x49, 0x4c, 0x4c, 0x5f, 0x44, 0x4d, 0x41, 0x10, 0x33, 0x12, 0x1a,
+     0x0a, 0x16, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x52,
+     0x45, 0x46, 0x49, 0x4c, 0x4c, 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c,
+     0x10, 0x34, 0x12, 0x1b, 0x0a, 0x17, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
+     0x5f, 0x50, 0x47, 0x52, 0x45, 0x46, 0x49, 0x4c, 0x4c, 0x5f, 0x4d, 0x4f,
+     0x56, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x35, 0x12, 0x1d, 0x0a, 0x19, 0x56,
      0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x54, 0x45, 0x41,
-     0x4c, 0x5f, 0x4b, 0x53, 0x57, 0x41, 0x50, 0x44, 0x5f, 0x4e, 0x4f, 0x52,
-     0x4d, 0x41, 0x4c, 0x10, 0x37, 0x12, 0x21, 0x0a, 0x1d, 0x56, 0x4d, 0x53,
-     0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x54, 0x45, 0x41, 0x4c, 0x5f,
-     0x4b, 0x53, 0x57, 0x41, 0x50, 0x44, 0x5f, 0x4d, 0x4f, 0x56, 0x41, 0x42,
-     0x4c, 0x45, 0x10, 0x38, 0x12, 0x1d, 0x0a, 0x19, 0x56, 0x4d, 0x53, 0x54,
-     0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x54, 0x45, 0x41, 0x4c, 0x5f, 0x44,
-     0x49, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x44, 0x4d, 0x41, 0x10, 0x39, 0x12,
-     0x20, 0x0a, 0x1c, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47,
+     0x4c, 0x5f, 0x4b, 0x53, 0x57, 0x41, 0x50, 0x44, 0x5f, 0x44, 0x4d, 0x41,
+     0x10, 0x36, 0x12, 0x20, 0x0a, 0x1c, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
+     0x5f, 0x50, 0x47, 0x53, 0x54, 0x45, 0x41, 0x4c, 0x5f, 0x4b, 0x53, 0x57,
+     0x41, 0x50, 0x44, 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x37,
+     0x12, 0x21, 0x0a, 0x1d, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50,
+     0x47, 0x53, 0x54, 0x45, 0x41, 0x4c, 0x5f, 0x4b, 0x53, 0x57, 0x41, 0x50,
+     0x44, 0x5f, 0x4d, 0x4f, 0x56, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x38, 0x12,
+     0x1d, 0x0a, 0x19, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47,
      0x53, 0x54, 0x45, 0x41, 0x4c, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54,
-     0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x3a, 0x12, 0x21, 0x0a,
-     0x1d, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x54,
-     0x45, 0x41, 0x4c, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x4d,
-     0x4f, 0x56, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x3b, 0x12, 0x1c, 0x0a, 0x18,
-     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x43, 0x41,
-     0x4e, 0x5f, 0x4b, 0x53, 0x57, 0x41, 0x50, 0x44, 0x5f, 0x44, 0x4d, 0x41,
-     0x10, 0x3c, 0x12, 0x1f, 0x0a, 0x1b, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
-     0x5f, 0x50, 0x47, 0x53, 0x43, 0x41, 0x4e, 0x5f, 0x4b, 0x53, 0x57, 0x41,
-     0x50, 0x44, 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x3d, 0x12,
-     0x20, 0x0a, 0x1c, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47,
-     0x53, 0x43, 0x41, 0x4e, 0x5f, 0x4b, 0x53, 0x57, 0x41, 0x50, 0x44, 0x5f,
-     0x4d, 0x4f, 0x56, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x3e, 0x12, 0x1c, 0x0a,
-     0x18, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x43,
-     0x41, 0x4e, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x44, 0x4d,
-     0x41, 0x10, 0x3f, 0x12, 0x1f, 0x0a, 0x1b, 0x56, 0x4d, 0x53, 0x54, 0x41,
-     0x54, 0x5f, 0x50, 0x47, 0x53, 0x43, 0x41, 0x4e, 0x5f, 0x44, 0x49, 0x52,
-     0x45, 0x43, 0x54, 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x40,
-     0x12, 0x20, 0x0a, 0x1c, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50,
-     0x47, 0x53, 0x43, 0x41, 0x4e, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54,
-     0x5f, 0x4d, 0x4f, 0x56, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x41, 0x12, 0x21,
-     0x0a, 0x1d, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53,
-     0x43, 0x41, 0x4e, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x54,
-     0x48, 0x52, 0x4f, 0x54, 0x54, 0x4c, 0x45, 0x10, 0x42, 0x12, 0x17, 0x0a,
-     0x13, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x49, 0x4e,
-     0x4f, 0x44, 0x45, 0x53, 0x54, 0x45, 0x41, 0x4c, 0x10, 0x43, 0x12, 0x18,
-     0x0a, 0x14, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x53, 0x4c, 0x41,
-     0x42, 0x53, 0x5f, 0x53, 0x43, 0x41, 0x4e, 0x4e, 0x45, 0x44, 0x10, 0x44,
-     0x12, 0x1c, 0x0a, 0x18, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4b,
-     0x53, 0x57, 0x41, 0x50, 0x44, 0x5f, 0x49, 0x4e, 0x4f, 0x44, 0x45, 0x53,
-     0x54, 0x45, 0x41, 0x4c, 0x10, 0x45, 0x12, 0x27, 0x0a, 0x23, 0x56, 0x4d,
+     0x5f, 0x44, 0x4d, 0x41, 0x10, 0x39, 0x12, 0x20, 0x0a, 0x1c, 0x56, 0x4d,
+     0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x54, 0x45, 0x41, 0x4c,
+     0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x4e, 0x4f, 0x52, 0x4d,
+     0x41, 0x4c, 0x10, 0x3a, 0x12, 0x21, 0x0a, 0x1d, 0x56, 0x4d, 0x53, 0x54,
+     0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x54, 0x45, 0x41, 0x4c, 0x5f, 0x44,
+     0x49, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x4d, 0x4f, 0x56, 0x41, 0x42, 0x4c,
+     0x45, 0x10, 0x3b, 0x12, 0x1c, 0x0a, 0x18, 0x56, 0x4d, 0x53, 0x54, 0x41,
+     0x54, 0x5f, 0x50, 0x47, 0x53, 0x43, 0x41, 0x4e, 0x5f, 0x4b, 0x53, 0x57,
+     0x41, 0x50, 0x44, 0x5f, 0x44, 0x4d, 0x41, 0x10, 0x3c, 0x12, 0x1f, 0x0a,
+     0x1b, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x43,
+     0x41, 0x4e, 0x5f, 0x4b, 0x53, 0x57, 0x41, 0x50, 0x44, 0x5f, 0x4e, 0x4f,
+     0x52, 0x4d, 0x41, 0x4c, 0x10, 0x3d, 0x12, 0x20, 0x0a, 0x1c, 0x56, 0x4d,
+     0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x43, 0x41, 0x4e, 0x5f,
+     0x4b, 0x53, 0x57, 0x41, 0x50, 0x44, 0x5f, 0x4d, 0x4f, 0x56, 0x41, 0x42,
+     0x4c, 0x45, 0x10, 0x3e, 0x12, 0x1c, 0x0a, 0x18, 0x56, 0x4d, 0x53, 0x54,
+     0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x43, 0x41, 0x4e, 0x5f, 0x44, 0x49,
+     0x52, 0x45, 0x43, 0x54, 0x5f, 0x44, 0x4d, 0x41, 0x10, 0x3f, 0x12, 0x1f,
+     0x0a, 0x1b, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53,
+     0x43, 0x41, 0x4e, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x4e,
+     0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x40, 0x12, 0x20, 0x0a, 0x1c, 0x56,
+     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x43, 0x41, 0x4e,
+     0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x4d, 0x4f, 0x56, 0x41,
+     0x42, 0x4c, 0x45, 0x10, 0x41, 0x12, 0x21, 0x0a, 0x1d, 0x56, 0x4d, 0x53,
+     0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x43, 0x41, 0x4e, 0x5f, 0x44,
+     0x49, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x54, 0x48, 0x52, 0x4f, 0x54, 0x54,
+     0x4c, 0x45, 0x10, 0x42, 0x12, 0x17, 0x0a, 0x13, 0x56, 0x4d, 0x53, 0x54,
+     0x41, 0x54, 0x5f, 0x50, 0x47, 0x49, 0x4e, 0x4f, 0x44, 0x45, 0x53, 0x54,
+     0x45, 0x41, 0x4c, 0x10, 0x43, 0x12, 0x18, 0x0a, 0x14, 0x56, 0x4d, 0x53,
+     0x54, 0x41, 0x54, 0x5f, 0x53, 0x4c, 0x41, 0x42, 0x53, 0x5f, 0x53, 0x43,
+     0x41, 0x4e, 0x4e, 0x45, 0x44, 0x10, 0x44, 0x12, 0x1c, 0x0a, 0x18, 0x56,
+     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4b, 0x53, 0x57, 0x41, 0x50, 0x44,
+     0x5f, 0x49, 0x4e, 0x4f, 0x44, 0x45, 0x53, 0x54, 0x45, 0x41, 0x4c, 0x10,
+     0x45, 0x12, 0x27, 0x0a, 0x23, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
+     0x4b, 0x53, 0x57, 0x41, 0x50, 0x44, 0x5f, 0x4c, 0x4f, 0x57, 0x5f, 0x57,
+     0x4d, 0x41, 0x52, 0x4b, 0x5f, 0x48, 0x49, 0x54, 0x5f, 0x51, 0x55, 0x49,
+     0x43, 0x4b, 0x4c, 0x59, 0x10, 0x46, 0x12, 0x28, 0x0a, 0x24, 0x56, 0x4d,
      0x53, 0x54, 0x41, 0x54, 0x5f, 0x4b, 0x53, 0x57, 0x41, 0x50, 0x44, 0x5f,
-     0x4c, 0x4f, 0x57, 0x5f, 0x57, 0x4d, 0x41, 0x52, 0x4b, 0x5f, 0x48, 0x49,
-     0x54, 0x5f, 0x51, 0x55, 0x49, 0x43, 0x4b, 0x4c, 0x59, 0x10, 0x46, 0x12,
-     0x28, 0x0a, 0x24, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4b, 0x53,
-     0x57, 0x41, 0x50, 0x44, 0x5f, 0x48, 0x49, 0x47, 0x48, 0x5f, 0x57, 0x4d,
-     0x41, 0x52, 0x4b, 0x5f, 0x48, 0x49, 0x54, 0x5f, 0x51, 0x55, 0x49, 0x43,
-     0x4b, 0x4c, 0x59, 0x10, 0x47, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x4d, 0x53,
-     0x54, 0x41, 0x54, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x4f, 0x55, 0x54, 0x52,
-     0x55, 0x4e, 0x10, 0x48, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x4d, 0x53, 0x54,
-     0x41, 0x54, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x53, 0x54, 0x41, 0x4c,
-     0x4c, 0x10, 0x49, 0x12, 0x14, 0x0a, 0x10, 0x56, 0x4d, 0x53, 0x54, 0x41,
-     0x54, 0x5f, 0x50, 0x47, 0x52, 0x4f, 0x54, 0x41, 0x54, 0x45, 0x44, 0x10,
-     0x4a, 0x12, 0x19, 0x0a, 0x15, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
-     0x44, 0x52, 0x4f, 0x50, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x43, 0x41, 0x43,
-     0x48, 0x45, 0x10, 0x4b, 0x12, 0x14, 0x0a, 0x10, 0x56, 0x4d, 0x53, 0x54,
-     0x41, 0x54, 0x5f, 0x44, 0x52, 0x4f, 0x50, 0x5f, 0x53, 0x4c, 0x41, 0x42,
-     0x10, 0x4c, 0x12, 0x1c, 0x0a, 0x18, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
-     0x5f, 0x50, 0x47, 0x4d, 0x49, 0x47, 0x52, 0x41, 0x54, 0x45, 0x5f, 0x53,
-     0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x4d, 0x12, 0x19, 0x0a, 0x15,
-     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x4d, 0x49, 0x47,
-     0x52, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x10, 0x4e, 0x12,
-     0x22, 0x0a, 0x1e, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x43, 0x4f,
-     0x4d, 0x50, 0x41, 0x43, 0x54, 0x5f, 0x4d, 0x49, 0x47, 0x52, 0x41, 0x54,
-     0x45, 0x5f, 0x53, 0x43, 0x41, 0x4e, 0x4e, 0x45, 0x44, 0x10, 0x4f, 0x12,
-     0x1f, 0x0a, 0x1b, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x43, 0x4f,
-     0x4d, 0x50, 0x41, 0x43, 0x54, 0x5f, 0x46, 0x52, 0x45, 0x45, 0x5f, 0x53,
-     0x43, 0x41, 0x4e, 0x4e, 0x45, 0x44, 0x10, 0x50, 0x12, 0x1b, 0x0a, 0x17,
+     0x48, 0x49, 0x47, 0x48, 0x5f, 0x57, 0x4d, 0x41, 0x52, 0x4b, 0x5f, 0x48,
+     0x49, 0x54, 0x5f, 0x51, 0x55, 0x49, 0x43, 0x4b, 0x4c, 0x59, 0x10, 0x47,
+     0x12, 0x15, 0x0a, 0x11, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50,
+     0x41, 0x47, 0x45, 0x4f, 0x55, 0x54, 0x52, 0x55, 0x4e, 0x10, 0x48, 0x12,
+     0x15, 0x0a, 0x11, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x41, 0x4c,
+     0x4c, 0x4f, 0x43, 0x53, 0x54, 0x41, 0x4c, 0x4c, 0x10, 0x49, 0x12, 0x14,
+     0x0a, 0x10, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x52,
+     0x4f, 0x54, 0x41, 0x54, 0x45, 0x44, 0x10, 0x4a, 0x12, 0x19, 0x0a, 0x15,
+     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x44, 0x52, 0x4f, 0x50, 0x5f,
+     0x50, 0x41, 0x47, 0x45, 0x43, 0x41, 0x43, 0x48, 0x45, 0x10, 0x4b, 0x12,
+     0x14, 0x0a, 0x10, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x44, 0x52,
+     0x4f, 0x50, 0x5f, 0x53, 0x4c, 0x41, 0x42, 0x10, 0x4c, 0x12, 0x1c, 0x0a,
+     0x18, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x4d, 0x49,
+     0x47, 0x52, 0x41, 0x54, 0x45, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53,
+     0x53, 0x10, 0x4d, 0x12, 0x19, 0x0a, 0x15, 0x56, 0x4d, 0x53, 0x54, 0x41,
+     0x54, 0x5f, 0x50, 0x47, 0x4d, 0x49, 0x47, 0x52, 0x41, 0x54, 0x45, 0x5f,
+     0x46, 0x41, 0x49, 0x4c, 0x10, 0x4e, 0x12, 0x22, 0x0a, 0x1e, 0x56, 0x4d,
+     0x53, 0x54, 0x41, 0x54, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x43, 0x54,
+     0x5f, 0x4d, 0x49, 0x47, 0x52, 0x41, 0x54, 0x45, 0x5f, 0x53, 0x43, 0x41,
+     0x4e, 0x4e, 0x45, 0x44, 0x10, 0x4f, 0x12, 0x1f, 0x0a, 0x1b, 0x56, 0x4d,
+     0x53, 0x54, 0x41, 0x54, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x43, 0x54,
+     0x5f, 0x46, 0x52, 0x45, 0x45, 0x5f, 0x53, 0x43, 0x41, 0x4e, 0x4e, 0x45,
+     0x44, 0x10, 0x50, 0x12, 0x1b, 0x0a, 0x17, 0x56, 0x4d, 0x53, 0x54, 0x41,
+     0x54, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x43, 0x54, 0x5f, 0x49, 0x53,
+     0x4f, 0x4c, 0x41, 0x54, 0x45, 0x44, 0x10, 0x51, 0x12, 0x18, 0x0a, 0x14,
      0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41,
-     0x43, 0x54, 0x5f, 0x49, 0x53, 0x4f, 0x4c, 0x41, 0x54, 0x45, 0x44, 0x10,
-     0x51, 0x12, 0x18, 0x0a, 0x14, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
-     0x43, 0x4f, 0x4d, 0x50, 0x41, 0x43, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x4c,
-     0x4c, 0x10, 0x52, 0x12, 0x17, 0x0a, 0x13, 0x56, 0x4d, 0x53, 0x54, 0x41,
-     0x54, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x43, 0x54, 0x5f, 0x46, 0x41,
-     0x49, 0x4c, 0x10, 0x53, 0x12, 0x1a, 0x0a, 0x16, 0x56, 0x4d, 0x53, 0x54,
-     0x41, 0x54, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x43, 0x54, 0x5f, 0x53,
-     0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x54, 0x12, 0x1e, 0x0a, 0x1a,
-     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41,
-     0x43, 0x54, 0x5f, 0x44, 0x41, 0x45, 0x4d, 0x4f, 0x4e, 0x5f, 0x57, 0x41,
-     0x4b, 0x45, 0x10, 0x55, 0x12, 0x21, 0x0a, 0x1d, 0x56, 0x4d, 0x53, 0x54,
-     0x41, 0x54, 0x5f, 0x55, 0x4e, 0x45, 0x56, 0x49, 0x43, 0x54, 0x41, 0x42,
-     0x4c, 0x45, 0x5f, 0x50, 0x47, 0x53, 0x5f, 0x43, 0x55, 0x4c, 0x4c, 0x45,
-     0x44, 0x10, 0x56, 0x12, 0x22, 0x0a, 0x1e, 0x56, 0x4d, 0x53, 0x54, 0x41,
-     0x54, 0x5f, 0x55, 0x4e, 0x45, 0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c,
-     0x45, 0x5f, 0x50, 0x47, 0x53, 0x5f, 0x53, 0x43, 0x41, 0x4e, 0x4e, 0x45,
-     0x44, 0x10, 0x57, 0x12, 0x22, 0x0a, 0x1e, 0x56, 0x4d, 0x53, 0x54, 0x41,
-     0x54, 0x5f, 0x55, 0x4e, 0x45, 0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c,
-     0x45, 0x5f, 0x50, 0x47, 0x53, 0x5f, 0x52, 0x45, 0x53, 0x43, 0x55, 0x45,
-     0x44, 0x10, 0x58, 0x12, 0x22, 0x0a, 0x1e, 0x56, 0x4d, 0x53, 0x54, 0x41,
-     0x54, 0x5f, 0x55, 0x4e, 0x45, 0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c,
-     0x45, 0x5f, 0x50, 0x47, 0x53, 0x5f, 0x4d, 0x4c, 0x4f, 0x43, 0x4b, 0x45,
-     0x44, 0x10, 0x59, 0x12, 0x24, 0x0a, 0x20, 0x56, 0x4d, 0x53, 0x54, 0x41,
-     0x54, 0x5f, 0x55, 0x4e, 0x45, 0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c,
-     0x45, 0x5f, 0x50, 0x47, 0x53, 0x5f, 0x4d, 0x55, 0x4e, 0x4c, 0x4f, 0x43,
-     0x4b, 0x45, 0x44, 0x10, 0x5a, 0x12, 0x22, 0x0a, 0x1e, 0x56, 0x4d, 0x53,
-     0x54, 0x41, 0x54, 0x5f, 0x55, 0x4e, 0x45, 0x56, 0x49, 0x43, 0x54, 0x41,
-     0x42, 0x4c, 0x45, 0x5f, 0x50, 0x47, 0x53, 0x5f, 0x43, 0x4c, 0x45, 0x41,
-     0x52, 0x45, 0x44, 0x10, 0x5b, 0x12, 0x23, 0x0a, 0x1f, 0x56, 0x4d, 0x53,
-     0x54, 0x41, 0x54, 0x5f, 0x55, 0x4e, 0x45, 0x56, 0x49, 0x43, 0x54, 0x41,
-     0x42, 0x4c, 0x45, 0x5f, 0x50, 0x47, 0x53, 0x5f, 0x53, 0x54, 0x52, 0x41,
-     0x4e, 0x44, 0x45, 0x44, 0x10, 0x5c, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x4d,
-     0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x5a, 0x53, 0x50, 0x41,
-     0x47, 0x45, 0x53, 0x10, 0x5d, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x4d, 0x53,
-     0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x49, 0x4f, 0x4e, 0x5f, 0x48,
-     0x45, 0x41, 0x50, 0x10, 0x5e, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x4d, 0x53,
-     0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x47, 0x50, 0x55, 0x5f, 0x48,
-     0x45, 0x41, 0x50, 0x10, 0x5f}};
+     0x43, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x4c, 0x4c, 0x10, 0x52, 0x12, 0x17,
+     0x0a, 0x13, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x43, 0x4f, 0x4d,
+     0x50, 0x41, 0x43, 0x54, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x10, 0x53, 0x12,
+     0x1a, 0x0a, 0x16, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x43, 0x4f,
+     0x4d, 0x50, 0x41, 0x43, 0x54, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53,
+     0x53, 0x10, 0x54, 0x12, 0x1e, 0x0a, 0x1a, 0x56, 0x4d, 0x53, 0x54, 0x41,
+     0x54, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x43, 0x54, 0x5f, 0x44, 0x41,
+     0x45, 0x4d, 0x4f, 0x4e, 0x5f, 0x57, 0x41, 0x4b, 0x45, 0x10, 0x55, 0x12,
+     0x21, 0x0a, 0x1d, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x55, 0x4e,
+     0x45, 0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x50, 0x47,
+     0x53, 0x5f, 0x43, 0x55, 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x56, 0x12, 0x22,
+     0x0a, 0x1e, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x55, 0x4e, 0x45,
+     0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x50, 0x47, 0x53,
+     0x5f, 0x53, 0x43, 0x41, 0x4e, 0x4e, 0x45, 0x44, 0x10, 0x57, 0x12, 0x22,
+     0x0a, 0x1e, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x55, 0x4e, 0x45,
+     0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x50, 0x47, 0x53,
+     0x5f, 0x52, 0x45, 0x53, 0x43, 0x55, 0x45, 0x44, 0x10, 0x58, 0x12, 0x22,
+     0x0a, 0x1e, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x55, 0x4e, 0x45,
+     0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x50, 0x47, 0x53,
+     0x5f, 0x4d, 0x4c, 0x4f, 0x43, 0x4b, 0x45, 0x44, 0x10, 0x59, 0x12, 0x24,
+     0x0a, 0x20, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x55, 0x4e, 0x45,
+     0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x50, 0x47, 0x53,
+     0x5f, 0x4d, 0x55, 0x4e, 0x4c, 0x4f, 0x43, 0x4b, 0x45, 0x44, 0x10, 0x5a,
+     0x12, 0x22, 0x0a, 0x1e, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x55,
+     0x4e, 0x45, 0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x50,
+     0x47, 0x53, 0x5f, 0x43, 0x4c, 0x45, 0x41, 0x52, 0x45, 0x44, 0x10, 0x5b,
+     0x12, 0x23, 0x0a, 0x1f, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x55,
+     0x4e, 0x45, 0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x50,
+     0x47, 0x53, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x4e, 0x44, 0x45, 0x44, 0x10,
+     0x5c, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
+     0x4e, 0x52, 0x5f, 0x5a, 0x53, 0x50, 0x41, 0x47, 0x45, 0x53, 0x10, 0x5d,
+     0x12, 0x16, 0x0a, 0x12, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e,
+     0x52, 0x5f, 0x49, 0x4f, 0x4e, 0x5f, 0x48, 0x45, 0x41, 0x50, 0x10, 0x5e,
+     0x12, 0x16, 0x0a, 0x12, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e,
+     0x52, 0x5f, 0x47, 0x50, 0x55, 0x5f, 0x48, 0x45, 0x41, 0x50, 0x10, 0x5f}};
 
 }  // namespace perfetto
 
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/common/BUILD.gn b/src/profiling/common/BUILD.gn
new file mode 100644
index 0000000..e999f29
--- /dev/null
+++ b/src/profiling/common/BUILD.gn
@@ -0,0 +1,94 @@
+# 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")
+
+source_set("unwind_support") {
+  public_deps = [ "../../../gn:libunwindstack" ]
+  deps = [
+    "../../../gn:default_deps",
+    "../../../src/base",
+  ]
+  sources = [
+    "unwind_support.cc",
+    "unwind_support.h",
+  ]
+}
+
+source_set("callstack_trie") {
+  public_deps = [ ":unwind_support" ]
+  deps = [
+    ":interner",
+    "../../../gn:default_deps",
+    "../../../src/base",
+  ]
+  sources = [
+    "callstack_trie.cc",
+    "callstack_trie.h",
+  ]
+}
+
+source_set("interner") {
+  deps = [
+    "../../../gn:default_deps",
+    "../../../src/base",
+  ]
+  sources = [ "interner.h" ]
+}
+
+source_set("interning_output") {
+  deps = [
+    ":callstack_trie",
+    ":interner",
+    "../../../gn:default_deps",
+    "../../../include/perfetto/ext/tracing/core",
+    "../../../protos/perfetto/trace:zero",
+    "../../../protos/perfetto/trace/interned_data:zero",
+    "../../../protos/perfetto/trace/profiling:zero",
+  ]
+  sources = [
+    "interning_output.cc",
+    "interning_output.h",
+  ]
+}
+
+source_set("proc_utils") {
+  deps = [
+    "../../../gn:default_deps",
+    "../../../include/perfetto/profiling:normalize",
+    "../../base",
+  ]
+  sources = [
+    "proc_utils.cc",
+    "proc_utils.h",
+  ]
+}
+
+perfetto_unittest_source_set("unittests") {
+  testonly = true
+  deps = [
+    ":interner",
+    ":proc_utils",
+    "../../../gn:default_deps",
+    "../../../gn:gtest_and_gmock",
+    "../../../include/perfetto/profiling:normalize",
+    "../../base",
+    "../../base:test_support",
+  ]
+  sources = [
+    "interner_unittest.cc",
+    "proc_utils_unittest.cc",
+  ]
+}
diff --git a/src/profiling/common/callstack_trie.cc b/src/profiling/common/callstack_trie.cc
new file mode 100644
index 0000000..01541e9
--- /dev/null
+++ b/src/profiling/common/callstack_trie.cc
@@ -0,0 +1,121 @@
+/*
+ * 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/profiling/common/callstack_trie.h"
+
+#include <vector>
+
+#include "perfetto/ext/base/string_splitter.h"
+#include "src/profiling/common/interner.h"
+#include "src/profiling/common/unwind_support.h"
+
+namespace perfetto {
+namespace profiling {
+
+GlobalCallstackTrie::Node* GlobalCallstackTrie::GetOrCreateChild(
+    Node* self,
+    const Interned<Frame>& loc) {
+  Node* child = self->children_.Get(loc);
+  if (!child)
+    child = self->children_.Emplace(loc, ++next_callstack_id_, self);
+  return child;
+}
+
+std::vector<Interned<Frame>> GlobalCallstackTrie::BuildInverseCallstack(
+    const Node* node) const {
+  std::vector<Interned<Frame>> res;
+  while (node != &root_) {
+    res.emplace_back(node->location_);
+    node = node->parent_;
+  }
+  return res;
+}
+
+GlobalCallstackTrie::Node* GlobalCallstackTrie::CreateCallsite(
+    const std::vector<FrameData>& callstack) {
+  Node* node = &root_;
+  // libunwindstack gives the frames top-first, but we want to bookkeep and
+  // emit as bottom first.
+  for (auto it = callstack.crbegin(); it != callstack.crend(); ++it) {
+    const FrameData& loc = *it;
+    node = GetOrCreateChild(node, InternCodeLocation(loc));
+  }
+  return node;
+}
+
+GlobalCallstackTrie::Node* GlobalCallstackTrie::CreateCallsite(
+    const std::vector<Interned<Frame>>& callstack) {
+  Node* node = &root_;
+  // libunwindstack gives the frames top-first, but we want to bookkeep and
+  // emit as bottom first.
+  for (auto it = callstack.crbegin(); it != callstack.crend(); ++it) {
+    const Interned<Frame>& loc = *it;
+    node = GetOrCreateChild(node, loc);
+  }
+  return node;
+}
+
+void GlobalCallstackTrie::IncrementNode(Node* node) {
+  while (node != nullptr) {
+    node->ref_count_ += 1;
+    node = node->parent_;
+  }
+}
+
+void GlobalCallstackTrie::DecrementNode(Node* node) {
+  PERFETTO_DCHECK(node->ref_count_ >= 1);
+
+  bool delete_prev = false;
+  Node* prev = nullptr;
+  while (node != nullptr) {
+    if (delete_prev)
+      node->children_.Remove(*prev);
+    node->ref_count_ -= 1;
+    delete_prev = node->ref_count_ == 0;
+    prev = node;
+    node = node->parent_;
+  }
+}
+
+Interned<Frame> GlobalCallstackTrie::InternCodeLocation(const FrameData& loc) {
+  Mapping map(string_interner_.Intern(loc.build_id));
+  map.exact_offset = loc.frame.map_exact_offset;
+  map.start_offset = loc.frame.map_elf_start_offset;
+  map.start = loc.frame.map_start;
+  map.end = loc.frame.map_end;
+  map.load_bias = loc.frame.map_load_bias;
+  base::StringSplitter sp(loc.frame.map_name, '/');
+  while (sp.Next())
+    map.path_components.emplace_back(string_interner_.Intern(sp.cur_token()));
+
+  Frame frame(mapping_interner_.Intern(std::move(map)),
+              string_interner_.Intern(loc.frame.function_name),
+              loc.frame.rel_pc);
+
+  return frame_interner_.Intern(frame);
+}
+
+Interned<Frame> GlobalCallstackTrie::MakeRootFrame() {
+  Mapping map(string_interner_.Intern(""));
+
+  Frame frame(mapping_interner_.Intern(std::move(map)),
+              string_interner_.Intern(""), 0);
+
+  return frame_interner_.Intern(frame);
+}
+
+}  // namespace profiling
+}  // namespace perfetto
diff --git a/src/profiling/common/callstack_trie.h b/src/profiling/common/callstack_trie.h
new file mode 100644
index 0000000..d711cf5
--- /dev/null
+++ b/src/profiling/common/callstack_trie.h
@@ -0,0 +1,203 @@
+/*
+ * 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_PROFILING_COMMON_CALLSTACK_TRIE_H_
+#define SRC_PROFILING_COMMON_CALLSTACK_TRIE_H_
+
+#include <string>
+#include <typeindex>
+#include <vector>
+
+#include "perfetto/ext/base/lookup_set.h"
+#include "src/profiling/common/interner.h"
+#include "src/profiling/common/unwind_support.h"
+
+namespace perfetto {
+namespace profiling {
+
+struct Mapping {
+  Mapping(Interned<std::string> b) : build_id(std::move(b)) {}
+
+  Interned<std::string> build_id;
+  uint64_t exact_offset = 0;
+  uint64_t start_offset = 0;
+  uint64_t start = 0;
+  uint64_t end = 0;
+  uint64_t load_bias = 0;
+  std::vector<Interned<std::string>> path_components{};
+
+  bool operator<(const Mapping& other) const {
+    return std::tie(build_id, exact_offset, start_offset, start, end, load_bias,
+                    path_components) <
+           std::tie(other.build_id, other.exact_offset, other.start_offset,
+                    other.start, other.end, other.load_bias,
+                    other.path_components);
+  }
+  bool operator==(const Mapping& other) const {
+    return std::tie(build_id, exact_offset, start_offset, start, end, load_bias,
+                    path_components) ==
+           std::tie(other.build_id, other.exact_offset, other.start_offset,
+                    other.start, other.end, other.load_bias,
+                    other.path_components);
+  }
+};
+
+struct Frame {
+  Frame(Interned<Mapping> m, Interned<std::string> fn_name, uint64_t pc)
+      : mapping(m), function_name(fn_name), rel_pc(pc) {}
+  Interned<Mapping> mapping;
+  Interned<std::string> function_name;
+  uint64_t rel_pc;
+
+  bool operator<(const Frame& other) const {
+    return std::tie(mapping, function_name, rel_pc) <
+           std::tie(other.mapping, other.function_name, other.rel_pc);
+  }
+
+  bool operator==(const Frame& other) const {
+    return std::tie(mapping, function_name, rel_pc) ==
+           std::tie(other.mapping, other.function_name, other.rel_pc);
+  }
+};
+
+// Graph of function callsites. A single instance can be used for callsites from
+// different processes. Each call site is represented by a
+// GlobalCallstackTrie::Node that is owned by the parent callsite. Each node has
+// a pointer to its parent, which means the function call-graph can be
+// reconstructed from a GlobalCallstackTrie::Node by walking down the parent
+// chain.
+//
+// For the following two callstacks:
+//  * libc_init -> main -> foo -> alloc_buf
+//  * libc_init -> main -> bar -> alloc_buf
+// The tree looks as following:
+//             alloc_buf  alloc_buf
+//                   |      |
+//                  foo    bar
+//                    \    /
+//                      main
+//                       |
+//                   libc_init
+//                       |
+//                    [root_]
+class GlobalCallstackTrie {
+ public:
+  // Optionally, Nodes can be externally refcounted via |IncrementNode| and
+  // |DecrementNode|. In which case, the orphaned nodes are deleted when the
+  // last reference is decremented.
+  class Node {
+   public:
+    // This is opaque except to GlobalCallstackTrie.
+    friend class GlobalCallstackTrie;
+
+    // Allow building a node out of a frame for base::LookupSet.
+    Node(Interned<Frame> frame) : Node(frame, 0, nullptr) {}
+    Node(Interned<Frame> frame, uint64_t id)
+        : Node(std::move(frame), id, nullptr) {}
+    Node(Interned<Frame> frame, uint64_t id, Node* parent)
+        : id_(id), parent_(parent), location_(frame) {}
+
+    ~Node() { PERFETTO_DCHECK(!ref_count_); }
+
+    uint64_t id() const { return id_; }
+
+   private:
+    Node* GetOrCreateChild(const Interned<Frame>& loc);
+    // Deletes all descendant nodes, regardless of |ref_count_|.
+    void DeleteChildren() { children_.Clear(); }
+
+    uint64_t ref_count_ = 0;
+    uint64_t id_;
+    Node* const parent_;
+    const Interned<Frame> location_;
+    base::LookupSet<Node, const Interned<Frame>, &Node::location_> children_;
+  };
+
+  GlobalCallstackTrie() = default;
+  ~GlobalCallstackTrie() = default;
+  GlobalCallstackTrie(const GlobalCallstackTrie&) = delete;
+  GlobalCallstackTrie& operator=(const GlobalCallstackTrie&) = delete;
+
+  // Moving this would invalidate the back pointers to the root_ node.
+  GlobalCallstackTrie(GlobalCallstackTrie&&) = delete;
+  GlobalCallstackTrie& operator=(GlobalCallstackTrie&&) = delete;
+
+  Interned<Frame> InternCodeLocation(const FrameData& loc);
+
+  Node* CreateCallsite(const std::vector<FrameData>& callstack);
+  Node* CreateCallsite(const std::vector<Interned<Frame>>& callstack);
+
+  static void IncrementNode(Node* node);
+  static void DecrementNode(Node* node);
+
+  std::vector<Interned<Frame>> BuildInverseCallstack(const Node* node) const;
+
+  // Purges all interned callstacks (and the associated internings), without
+  // restarting any interning sequences. Incompatible with external refcounting
+  // of nodes (Node.ref_count_).
+  void ClearTrie() {
+    PERFETTO_DLOG("Clearing trie");
+    root_.DeleteChildren();
+  }
+
+ private:
+  Node* GetOrCreateChild(Node* self, const Interned<Frame>& loc);
+
+  Interned<Frame> MakeRootFrame();
+
+  Interner<std::string> string_interner_;
+  Interner<Mapping> mapping_interner_;
+  Interner<Frame> frame_interner_;
+
+  uint64_t next_callstack_id_ = 0;
+
+  Node root_{MakeRootFrame(), ++next_callstack_id_};
+};
+
+}  // namespace profiling
+}  // namespace perfetto
+
+template <>
+struct std::hash<::perfetto::profiling::Mapping> {
+  using argument_type = ::perfetto::profiling::Mapping;
+  using result_type = size_t;
+  result_type operator()(const argument_type& mapping) {
+    size_t h =
+        std::hash<::perfetto::profiling::InternID>{}(mapping.build_id.id());
+    h ^= std::hash<uint64_t>{}(mapping.exact_offset);
+    h ^= std::hash<uint64_t>{}(mapping.start_offset);
+    h ^= std::hash<uint64_t>{}(mapping.start);
+    h ^= std::hash<uint64_t>{}(mapping.end);
+    h ^= std::hash<uint64_t>{}(mapping.load_bias);
+    for (const auto& path : mapping.path_components)
+      h ^= std::hash<uint64_t>{}(path.id());
+    return h;
+  }
+};
+
+template <>
+struct std::hash<::perfetto::profiling::Frame> {
+  using argument_type = ::perfetto::profiling::Frame;
+  using result_type = size_t;
+  result_type operator()(const argument_type& frame) {
+    size_t h = std::hash<::perfetto::profiling::InternID>{}(frame.mapping.id());
+    h ^= std::hash<::perfetto::profiling::InternID>{}(frame.function_name.id());
+    h ^= std::hash<uint64_t>{}(frame.rel_pc);
+    return h;
+  }
+};
+
+#endif  // SRC_PROFILING_COMMON_CALLSTACK_TRIE_H_
diff --git a/src/profiling/memory/interner.h b/src/profiling/common/interner.h
similarity index 95%
rename from src/profiling/memory/interner.h
rename to src/profiling/common/interner.h
index 2d3c677..7490ef2 100644
--- a/src/profiling/memory/interner.h
+++ b/src/profiling/common/interner.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_PROFILING_MEMORY_INTERNER_H_
-#define SRC_PROFILING_MEMORY_INTERNER_H_
+#ifndef SRC_PROFILING_COMMON_INTERNER_H_
+#define SRC_PROFILING_COMMON_INTERNER_H_
 
 #include <stddef.h>
 #include <stdint.h>
@@ -29,6 +29,7 @@
 
 using InternID = uint32_t;
 
+// Interner that hands out refcounted references.
 template <typename T>
 class Interner {
  private:
@@ -121,6 +122,7 @@
     if (--entry->ref_count == 0)
       entries_.erase(*entry);
   }
+
   InternID next_id = 1;
   std::unordered_set<Entry, typename Entry::Hash> entries_;
   static_assert(sizeof(Interned) == sizeof(void*),
@@ -138,4 +140,4 @@
 }  // namespace profiling
 }  // namespace perfetto
 
-#endif  // SRC_PROFILING_MEMORY_INTERNER_H_
+#endif  // SRC_PROFILING_COMMON_INTERNER_H_
diff --git a/src/profiling/memory/interner_unittest.cc b/src/profiling/common/interner_unittest.cc
similarity index 98%
rename from src/profiling/memory/interner_unittest.cc
rename to src/profiling/common/interner_unittest.cc
index e43b348..f7f1c21 100644
--- a/src/profiling/memory/interner_unittest.cc
+++ b/src/profiling/common/interner_unittest.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "src/profiling/memory/interner.h"
+#include "src/profiling/common/interner.h"
 
 #include "test/gtest_and_gmock.h"
 
diff --git a/src/profiling/common/interning_output.cc b/src/profiling/common/interning_output.cc
new file mode 100644
index 0000000..778f61e
--- /dev/null
+++ b/src/profiling/common/interning_output.cc
@@ -0,0 +1,172 @@
+/*
+ * 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/profiling/common/interning_output.h"
+
+#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
+#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
+#include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
+namespace {
+// Flags used to distinguish distinct types of interned strings.
+constexpr int kDumpedBuildID = 1 << 0;
+constexpr int kDumpedMappingPath = 1 << 1;
+constexpr int kDumpedFunctionName = 1 << 2;
+}  // namespace
+
+namespace perfetto {
+namespace profiling {
+
+// static
+void InterningOutputTracker::WriteFixedInterningsPacket(
+    TraceWriter* trace_writer) {
+  constexpr const uint8_t kEmptyString[] = "";
+  // Explicitly reserve intern ID 0 for the empty string, so unset string
+  // fields get mapped to this.
+  auto packet = trace_writer->NewTracePacket();
+  auto* interned_data = packet->set_interned_data();
+  auto* interned_string = interned_data->add_build_ids();
+  interned_string->set_iid(0);
+  interned_string->set_str(kEmptyString, 0);
+
+  interned_string = interned_data->add_mapping_paths();
+  interned_string->set_iid(0);
+  interned_string->set_str(kEmptyString, 0);
+
+  interned_string = interned_data->add_function_names();
+  interned_string->set_iid(0);
+  interned_string->set_str(kEmptyString, 0);
+
+  packet->set_incremental_state_cleared(true);
+}
+
+void InterningOutputTracker::WriteMap(const Interned<Mapping> map,
+                                      protos::pbzero::InternedData* out) {
+  auto map_it_and_inserted = dumped_mappings_.emplace(map.id());
+  if (map_it_and_inserted.second) {
+    for (const Interned<std::string>& str : map->path_components)
+      WriteMappingPathString(str, out);
+
+    WriteBuildIDString(map->build_id, out);
+
+    protos::pbzero::Mapping* mapping = out->add_mappings();
+    mapping->set_iid(map.id());
+    mapping->set_exact_offset(map->exact_offset);
+    mapping->set_start_offset(map->start_offset);
+    mapping->set_start(map->start);
+    mapping->set_end(map->end);
+    mapping->set_load_bias(map->load_bias);
+    mapping->set_build_id(map->build_id.id());
+    for (const Interned<std::string>& str : map->path_components)
+      mapping->add_path_string_ids(str.id());
+  }
+}
+
+void InterningOutputTracker::WriteFrame(Interned<Frame> frame,
+                                        protos::pbzero::InternedData* out) {
+  WriteMap(frame->mapping, out);
+  WriteFunctionNameString(frame->function_name, out);
+  bool inserted;
+  std::tie(std::ignore, inserted) = dumped_frames_.emplace(frame.id());
+  if (inserted) {
+    protos::pbzero::Frame* frame_proto = out->add_frames();
+    frame_proto->set_iid(frame.id());
+    frame_proto->set_function_name_id(frame->function_name.id());
+    frame_proto->set_mapping_id(frame->mapping.id());
+    frame_proto->set_rel_pc(frame->rel_pc);
+  }
+}
+
+void InterningOutputTracker::WriteBuildIDString(
+    const Interned<std::string>& str,
+    protos::pbzero::InternedData* out) {
+  auto it_and_inserted = dumped_strings_.emplace(str.id(), 0);
+  auto it = it_and_inserted.first;
+  // This is for the rare case that the same string is used as two different
+  // types (e.g. a function name that matches a path segment). In that case
+  // we need to emit the string as all of its types.
+  if ((it->second & kDumpedBuildID) == 0) {
+    protos::pbzero::InternedString* interned_string = out->add_build_ids();
+    interned_string->set_iid(str.id());
+    interned_string->set_str(str.data());
+    it->second |= kDumpedBuildID;
+  }
+}
+
+void InterningOutputTracker::WriteMappingPathString(
+    const Interned<std::string>& str,
+    protos::pbzero::InternedData* out) {
+  auto it_and_inserted = dumped_strings_.emplace(str.id(), 0);
+  auto it = it_and_inserted.first;
+  // This is for the rare case that the same string is used as two different
+  // types (e.g. a function name that matches a path segment). In that case
+  // we need to emit the string as all of its types.
+  if ((it->second & kDumpedMappingPath) == 0) {
+    protos::pbzero::InternedString* interned_string = out->add_mapping_paths();
+    interned_string->set_iid(str.id());
+    interned_string->set_str(str.data());
+    it->second |= kDumpedMappingPath;
+  }
+}
+
+void InterningOutputTracker::WriteFunctionNameString(
+    const Interned<std::string>& str,
+    protos::pbzero::InternedData* out) {
+  auto it_and_inserted = dumped_strings_.emplace(str.id(), 0);
+  auto it = it_and_inserted.first;
+  // This is for the rare case that the same string is used as two different
+  // types (e.g. a function name that matches a path segment). In that case
+  // we need to emit the string as all of its types.
+  if ((it->second & kDumpedFunctionName) == 0) {
+    protos::pbzero::InternedString* interned_string = out->add_function_names();
+    interned_string->set_iid(str.id());
+    interned_string->set_str(str.data());
+    it->second |= kDumpedFunctionName;
+  }
+}
+
+void InterningOutputTracker::WriteCallstack(GlobalCallstackTrie::Node* node,
+                                            GlobalCallstackTrie* trie,
+                                            protos::pbzero::InternedData* out) {
+  bool inserted;
+  std::tie(std::ignore, inserted) = dumped_callstacks_.emplace(node->id());
+  if (inserted) {
+    // There need to be two separate loops over built_callstack because
+    // protozero cannot interleave different messages.
+    auto built_callstack = trie->BuildInverseCallstack(node);
+    for (const Interned<Frame>& frame : built_callstack)
+      WriteFrame(frame, out);
+
+    protos::pbzero::Callstack* callstack = out->add_callstacks();
+    callstack->set_iid(node->id());
+    for (auto frame_it = built_callstack.crbegin();
+         frame_it != built_callstack.crend(); ++frame_it) {
+      const Interned<Frame>& frame = *frame_it;
+      callstack->add_frame_ids(frame.id());
+    }
+  }
+}
+
+void InterningOutputTracker::ClearHistory() {
+  dumped_strings_.clear();
+  dumped_frames_.clear();
+  dumped_mappings_.clear();
+  dumped_callstacks_.clear();
+}
+
+}  // namespace profiling
+}  // namespace perfetto
diff --git a/src/profiling/common/interning_output.h b/src/profiling/common/interning_output.h
new file mode 100644
index 0000000..cb92e52
--- /dev/null
+++ b/src/profiling/common/interning_output.h
@@ -0,0 +1,79 @@
+/*
+ * 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_PROFILING_COMMON_INTERNING_OUTPUT_H_
+#define SRC_PROFILING_COMMON_INTERNING_OUTPUT_H_
+
+#include <map>
+#include <set>
+
+#include <stdint.h>
+
+#include "perfetto/ext/tracing/core/trace_writer.h"
+#include "src/profiling/common/callstack_trie.h"
+#include "src/profiling/common/interner.h"
+
+#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
+
+namespace perfetto {
+namespace profiling {
+
+class InterningOutputTracker {
+ public:
+  // Writes out a full packet containing the "empty" (zero) internings.
+  // NB: resulting packet has |incremental_state_cleared| set.
+  static void WriteFixedInterningsPacket(TraceWriter* trace_writer);
+
+  void WriteMap(const Interned<Mapping> map, protos::pbzero::InternedData* out);
+  void WriteFrame(Interned<Frame> frame, protos::pbzero::InternedData* out);
+  void WriteBuildIDString(const Interned<std::string>& str,
+                          protos::pbzero::InternedData* out);
+  void WriteMappingPathString(const Interned<std::string>& str,
+                              protos::pbzero::InternedData* out);
+  void WriteFunctionNameString(const Interned<std::string>& str,
+                               protos::pbzero::InternedData* out);
+
+  // Writes out the callstack represented by the given node.
+  void WriteCallstack(GlobalCallstackTrie::Node* node,
+                      GlobalCallstackTrie* trie,
+                      protos::pbzero::InternedData* out);
+
+  bool IsCallstackNew(uint64_t callstack_id) {
+    return dumped_callstacks_.find(callstack_id) == dumped_callstacks_.end();
+  }
+
+  void ClearHistory();
+
+  // TODO(rsavitski): move elsewhere, used in heapprofd for orthogonal
+  // reasons. Shouldn't be cleared together with the rest of the incremental
+  // state.
+  uint64_t* HeapprofdNextIndexMutable() { return &next_index_; }
+
+ private:
+  // Map value is a bitfield distinguishing the distinct string fields
+  // the string can be emitted as, e.g. kDumpedBuildID.
+  std::map<InternID, int> dumped_strings_;
+  std::set<InternID> dumped_frames_;
+  std::set<InternID> dumped_mappings_;
+  std::set<uint64_t> dumped_callstacks_;  // uses callstack trie's node ids
+
+  uint64_t next_index_ = 0;
+};
+
+}  // namespace profiling
+}  // namespace perfetto
+
+#endif  // SRC_PROFILING_COMMON_INTERNING_OUTPUT_H_
diff --git a/src/profiling/memory/proc_utils.cc b/src/profiling/common/proc_utils.cc
similarity index 61%
rename from src/profiling/memory/proc_utils.cc
rename to src/profiling/common/proc_utils.cc
index 6a4c16b..8a9561a 100644
--- a/src/profiling/memory/proc_utils.cc
+++ b/src/profiling/common/proc_utils.cc
@@ -14,13 +14,14 @@
  * limitations under the License.
  */
 
-#include "src/profiling/memory/proc_utils.h"
+#include "src/profiling/common/proc_utils.h"
 
 #include <inttypes.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
 #include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/optional.h"
 #include "perfetto/profiling/normalize.h"
 
 namespace perfetto {
@@ -39,26 +40,42 @@
   return true;
 }
 
+base::Optional<uint32_t> ParseProcStatusSize(const std::string& status,
+                                             const std::string& key) {
+  auto entry_idx = status.find(key);
+  if (entry_idx == std::string::npos)
+    return {};
+  entry_idx = status.find_first_not_of(" \t", entry_idx + key.size());
+  if (entry_idx == std::string::npos)
+    return {};
+  int32_t val = atoi(status.c_str() + entry_idx);
+  if (val < 0) {
+    PERFETTO_ELOG("Unexpected value reading %s", key.c_str());
+    return {};
+  }
+  return static_cast<uint32_t>(val);
+}
 }  // namespace
 
-std::vector<std::string> NormalizeCmdlines(
+base::Optional<std::vector<std::string>> NormalizeCmdlines(
     const std::vector<std::string>& cmdlines) {
   std::vector<std::string> normalized_cmdlines;
-  for (std::string cmdline : cmdlines) {
+  normalized_cmdlines.reserve(cmdlines.size());
+
+  for (size_t i = 0; i < cmdlines.size(); i++) {
+    std::string cmdline = cmdlines[i];  // mutable copy
     // Add nullbyte to make sure it's a C string.
     cmdline.resize(cmdline.size() + 1, '\0');
-    std::string normalized;
     char* cmdline_cstr = &(cmdline[0]);
     ssize_t size = NormalizeCmdLine(&cmdline_cstr, cmdline.size());
     if (size == -1) {
-      PERFETTO_PLOG("Failed to normalize cmdline %s. Skipping.",
-                    cmdline.c_str());
-      continue;
+      PERFETTO_PLOG("Failed to normalize cmdline %s. Stopping the parse.",
+                    cmdlines[i].c_str());
+      return base::nullopt;
     }
-    normalized_cmdlines.emplace_back(
-        std::string(cmdline_cstr, static_cast<size_t>(size)));
+    normalized_cmdlines.emplace_back(cmdline_cstr, static_cast<size_t>(size));
   }
-  return normalized_cmdlines;
+  return base::make_optional(normalized_cmdlines);
 }
 
 // This is mostly the same as GetHeapprofdProgramProperty in
@@ -68,11 +85,7 @@
   std::string filename = "/proc/" + std::to_string(pid) + "/cmdline";
   base::ScopedFile fd(base::OpenFile(filename, O_RDONLY | O_CLOEXEC));
   if (!fd) {
-    // We do not expect errors other than permission errors here.
-    if (errno != EPERM && errno != EACCES)
-      PERFETTO_PLOG("Failed to open %s", filename.c_str());
-    else
-      PERFETTO_DPLOG("Failed to open %s", filename.c_str());
+    PERFETTO_DPLOG("Failed to open %s", filename.c_str());
     return false;
   }
   char cmdline[512];
@@ -140,5 +153,48 @@
   });
 }
 
+base::Optional<uint32_t> GetRssAnonAndSwap(pid_t pid) {
+  std::string path = "/proc/" + std::to_string(pid) + "/status";
+  std::string status;
+  bool read_proc = base::ReadFile(path, &status);
+  if (!read_proc) {
+    PERFETTO_ELOG("Failed to read %s", path.c_str());
+    return base::nullopt;
+  }
+  auto anon_rss = ParseProcStatusSize(status, "RssAnon:");
+  auto swap = ParseProcStatusSize(status, "VmSwap:");
+  if (anon_rss.has_value() && swap.has_value()) {
+    return *anon_rss + *swap;
+  }
+  return base::nullopt;
+}
+
+void RemoveUnderAnonThreshold(uint32_t min_size_kb, std::set<pid_t>* pids) {
+  for (auto it = pids->begin(); it != pids->end();) {
+    base::Optional<uint32_t> rss_and_swap = GetRssAnonAndSwap(*it);
+    if (rss_and_swap && rss_and_swap < min_size_kb) {
+      it = pids->erase(it);
+    } else {
+      ++it;
+    }
+  }
+}
+
+bool IsUnderAnonRssAndSwapThreshold(pid_t pid,
+                                    uint32_t min_size_kb,
+                                    const std::string& status) {
+  auto anon_rss = ParseProcStatusSize(status, "RssAnon:");
+  auto swap = ParseProcStatusSize(status, "VmSwap:");
+  if (anon_rss.has_value() && swap.has_value()) {
+    uint32_t anon_kb = anon_rss.value() + swap.value();
+    if (anon_kb < min_size_kb) {
+      PERFETTO_LOG("Removing pid %d from profiled set (anon: %d kB)", pid,
+                   anon_kb);
+      return true;
+    }
+  }
+  return false;
+}
+
 }  // namespace profiling
 }  // namespace perfetto
diff --git a/src/profiling/memory/proc_utils.h b/src/profiling/common/proc_utils.h
similarity index 69%
rename from src/profiling/memory/proc_utils.h
rename to src/profiling/common/proc_utils.h
index e123194..c658ead 100644
--- a/src/profiling/memory/proc_utils.h
+++ b/src/profiling/common/proc_utils.h
@@ -14,13 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef SRC_PROFILING_MEMORY_PROC_UTILS_H_
-#define SRC_PROFILING_MEMORY_PROC_UTILS_H_
+#ifndef SRC_PROFILING_COMMON_PROC_UTILS_H_
+#define SRC_PROFILING_COMMON_PROC_UTILS_H_
 
 #include <sys/types.h>
 #include <set>
 #include <vector>
 
+#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
 
 namespace perfetto {
@@ -43,7 +44,7 @@
   }
 }
 
-std::vector<std::string> NormalizeCmdlines(
+base::Optional<std::vector<std::string>> NormalizeCmdlines(
     const std::vector<std::string>& cmdlines);
 
 void FindAllProfilablePids(std::set<pid_t>* pids);
@@ -51,7 +52,15 @@
                          std::set<pid_t>* pids);
 bool GetCmdlineForPID(pid_t pid, std::string* name);
 
+base::Optional<uint32_t> GetRssAnonAndSwap(pid_t pid);
+// Filters the list of pids (in-place), keeping only the
+// entries satisfying the minimum size criteria for anonymous memory.
+void RemoveUnderAnonThreshold(uint32_t min_size_kb, std::set<pid_t>* pids);
+bool IsUnderAnonRssAndSwapThreshold(pid_t pid,
+                                    uint32_t min_size_kb,
+                                    const std::string& status);
+
 }  // namespace profiling
 }  // namespace perfetto
 
-#endif  // SRC_PROFILING_MEMORY_PROC_UTILS_H_
+#endif  // SRC_PROFILING_COMMON_PROC_UTILS_H_
diff --git a/src/profiling/memory/proc_utils_unittest.cc b/src/profiling/common/proc_utils_unittest.cc
similarity index 86%
rename from src/profiling/memory/proc_utils_unittest.cc
rename to src/profiling/common/proc_utils_unittest.cc
index 6c0065c..3d049f9 100644
--- a/src/profiling/memory/proc_utils_unittest.cc
+++ b/src/profiling/common/proc_utils_unittest.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "src/profiling/memory/proc_utils.h"
+#include "src/profiling/common/proc_utils.h"
 #include "perfetto/profiling/normalize.h"
 
 #include "perfetto/ext/base/utils.h"
@@ -125,6 +125,17 @@
   PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
 }
 
+TEST(ProcUtilsTest, AnonThreshold) {
+  std::string status = "Name: foo\nRssAnon:  10000 kB\nVmSwap:\t10000 kB";
+  EXPECT_FALSE(IsUnderAnonRssAndSwapThreshold(123, 20000, status));
+  EXPECT_TRUE(IsUnderAnonRssAndSwapThreshold(123, 20001, status));
+}
+
+TEST(ProcUtilsTest, AnonThresholdInvalidInput) {
+  EXPECT_FALSE(IsUnderAnonRssAndSwapThreshold(123, 20000, ""));
+  EXPECT_FALSE(IsUnderAnonRssAndSwapThreshold(123, 20000, "RssAnon: 10000 kB"));
+  EXPECT_FALSE(IsUnderAnonRssAndSwapThreshold(123, 20000, "VmSwap: 10000 kB"));
+}
 }  // namespace
 }  // namespace profiling
 }  // namespace perfetto
diff --git a/src/profiling/common/unwind_support.cc b/src/profiling/common/unwind_support.cc
new file mode 100644
index 0000000..606d54c
--- /dev/null
+++ b/src/profiling/common/unwind_support.cc
@@ -0,0 +1,153 @@
+/*
+ * 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/profiling/common/unwind_support.h"
+
+#include <inttypes.h>
+
+#include <procinfo/process_map.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "perfetto/ext/base/file_utils.h"
+
+namespace perfetto {
+namespace profiling {
+
+StackOverlayMemory::StackOverlayMemory(std::shared_ptr<unwindstack::Memory> mem,
+                                       uint64_t sp,
+                                       const uint8_t* stack,
+                                       size_t size)
+    : mem_(std::move(mem)), sp_(sp), stack_end_(sp + size), stack_(stack) {}
+
+size_t StackOverlayMemory::Read(uint64_t addr, void* dst, size_t size) {
+  if (addr >= sp_ && addr + size <= stack_end_ && addr + size > sp_) {
+    size_t offset = static_cast<size_t>(addr - sp_);
+    memcpy(dst, stack_ + offset, size);
+    return size;
+  }
+
+  return mem_->Read(addr, dst, size);
+}
+
+FDMemory::FDMemory(base::ScopedFile mem_fd) : mem_fd_(std::move(mem_fd)) {}
+
+size_t FDMemory::Read(uint64_t addr, void* dst, size_t size) {
+  ssize_t rd = pread64(*mem_fd_, dst, size, static_cast<off64_t>(addr));
+  if (rd == -1) {
+    PERFETTO_DPLOG("read of %zu at offset %" PRIu64, size, addr);
+    return 0;
+  }
+  return static_cast<size_t>(rd);
+}
+
+FDMaps::FDMaps(base::ScopedFile fd) : fd_(std::move(fd)) {}
+
+bool FDMaps::Parse() {
+  // If the process has already exited, lseek or ReadFileDescriptor will
+  // return false.
+  if (lseek(*fd_, 0, SEEK_SET) == -1)
+    return false;
+
+  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) {
+        // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
+        if (strncmp(name, "/dev/", 5) == 0 &&
+            strncmp(name + 5, "ashmem/", 7) != 0) {
+          flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
+        }
+        maps_.emplace_back(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;
+        }
+      });
+}
+
+void FDMaps::Reset() {
+  maps_.clear();
+}
+
+UnwindingMetadata::UnwindingMetadata(base::ScopedFile maps_fd,
+                                     base::ScopedFile mem_fd)
+    : fd_maps(std::move(maps_fd)),
+      fd_mem(std::make_shared<FDMemory>(std::move(mem_fd)))
+#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+      ,
+      jit_debug(std::unique_ptr<unwindstack::JitDebug>(
+          new unwindstack::JitDebug(fd_mem))),
+      dex_files(std::unique_ptr<unwindstack::DexFiles>(
+          new unwindstack::DexFiles(fd_mem)))
+#endif
+{
+  if (!fd_maps.Parse())
+    PERFETTO_DLOG("Failed initial maps parse");
+}
+
+void UnwindingMetadata::ReparseMaps() {
+  reparses++;
+  fd_maps.Reset();
+  fd_maps.Parse();
+#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+  jit_debug =
+      std::unique_ptr<unwindstack::JitDebug>(new unwindstack::JitDebug(fd_mem));
+  dex_files =
+      std::unique_ptr<unwindstack::DexFiles>(new unwindstack::DexFiles(fd_mem));
+#endif
+}
+
+FrameData UnwindingMetadata::AnnotateFrame(unwindstack::FrameData frame) {
+  std::string build_id;
+  if (!frame.map_name.empty()) {
+    unwindstack::MapInfo* map_info = fd_maps.Find(frame.pc);
+    if (map_info)
+      build_id = map_info->GetBuildID();
+  }
+
+  return FrameData{std::move(frame), std::move(build_id)};
+}
+
+std::string StringifyLibUnwindstackError(unwindstack::ErrorCode e) {
+  switch (e) {
+    case unwindstack::ERROR_NONE:
+      return "NONE";
+    case unwindstack::ERROR_MEMORY_INVALID:
+      return "MEMORY_INVALID";
+    case unwindstack::ERROR_UNWIND_INFO:
+      return "UNWIND_INFO";
+    case unwindstack::ERROR_UNSUPPORTED:
+      return "UNSUPPORTED";
+    case unwindstack::ERROR_INVALID_MAP:
+      return "INVALID_MAP";
+    case unwindstack::ERROR_MAX_FRAMES_EXCEEDED:
+      return "MAX_FRAME_EXCEEDED";
+    case unwindstack::ERROR_REPEATED_FRAME:
+      return "REPEATED_FRAME";
+    case unwindstack::ERROR_INVALID_ELF:
+      return "INVALID_ELF";
+  }
+}
+
+}  // namespace profiling
+}  // namespace perfetto
diff --git a/src/profiling/common/unwind_support.h b/src/profiling/common/unwind_support.h
new file mode 100644
index 0000000..76a2652
--- /dev/null
+++ b/src/profiling/common/unwind_support.h
@@ -0,0 +1,133 @@
+/*
+ * 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_PROFILING_COMMON_UNWIND_SUPPORT_H_
+#define SRC_PROFILING_COMMON_UNWIND_SUPPORT_H_
+
+// defines PERFETTO_BUILDFLAG
+#include "perfetto/base/build_config.h"
+
+#include <memory>
+#include <string>
+
+#include <unwindstack/Maps.h>
+#include <unwindstack/Unwinder.h>
+#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/JitDebug.h>
+#endif
+
+#include "perfetto/base/logging.h"
+#include "perfetto/base/time.h"
+#include "perfetto/ext/base/scoped_file.h"
+
+namespace perfetto {
+namespace profiling {
+
+// libunwindstack's FrameData annotated with the build_id.
+struct FrameData {
+  FrameData(unwindstack::FrameData f, std::string id)
+      : frame(std::move(f)), build_id(std::move(id)) {}
+
+  unwindstack::FrameData frame;
+  std::string build_id;
+};
+
+// Read /proc/[pid]/maps from an open file descriptor.
+// TODO(fmayer): Figure out deduplication to other maps.
+class FDMaps : public unwindstack::Maps {
+ public:
+  FDMaps(base::ScopedFile fd);
+
+  FDMaps(const FDMaps&) = delete;
+  FDMaps& operator=(const FDMaps&) = delete;
+
+  FDMaps(FDMaps&& m) : Maps(std::move(m)) { fd_ = std::move(m.fd_); }
+
+  FDMaps& operator=(FDMaps&& m) {
+    if (&m != this)
+      fd_ = std::move(m.fd_);
+    Maps::operator=(std::move(m));
+    return *this;
+  }
+
+  virtual ~FDMaps() override = default;
+
+  bool Parse() override;
+  void Reset();
+
+ private:
+  base::ScopedFile fd_;
+};
+
+class FDMemory : public unwindstack::Memory {
+ public:
+  FDMemory(base::ScopedFile mem_fd);
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+  base::ScopedFile mem_fd_;
+};
+
+// Overlays size bytes pointed to by stack for addresses in [sp, sp + size).
+// Addresses outside of that range are read from mem_fd, which should be an fd
+// that opened /proc/[pid]/mem.
+class StackOverlayMemory : public unwindstack::Memory {
+ public:
+  StackOverlayMemory(std::shared_ptr<unwindstack::Memory> mem,
+                     uint64_t sp,
+                     const uint8_t* stack,
+                     size_t size);
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+  std::shared_ptr<unwindstack::Memory> mem_;
+  const uint64_t sp_;
+  const uint64_t stack_end_;
+  const uint8_t* const stack_;
+};
+
+struct UnwindingMetadata {
+  UnwindingMetadata(base::ScopedFile maps_fd, base::ScopedFile mem_fd);
+
+  // move-only
+  UnwindingMetadata(const UnwindingMetadata&) = delete;
+  UnwindingMetadata& operator=(const UnwindingMetadata&) = delete;
+
+  UnwindingMetadata(UnwindingMetadata&&) = default;
+  UnwindingMetadata& operator=(UnwindingMetadata&&) = default;
+
+  void ReparseMaps();
+
+  FrameData AnnotateFrame(unwindstack::FrameData frame);
+
+  FDMaps fd_maps;
+  // 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;
+#endif
+};
+
+std::string StringifyLibUnwindstackError(unwindstack::ErrorCode);
+
+}  // namespace profiling
+}  // namespace perfetto
+
+#endif  // SRC_PROFILING_COMMON_UNWIND_SUPPORT_H_
diff --git a/src/profiling/memory/BUILD.gn b/src/profiling/memory/BUILD.gn
index 1862fb4..18f81b3 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,36 +46,30 @@
   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.
   source_set("malloc_hooks") {
     deps = [
       ":client",
-      ":proc_utils",
       ":scoped_spinlock",
       ":wire_protocol",
       "../../../gn:default_deps",
       "../../base",
       "../../base:unix_socket",
+      "../common:proc_utils",
     ]
     cflags = [
       "-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",
@@ -90,18 +82,6 @@
   ]
 }
 
-source_set("proc_utils") {
-  deps = [
-    "../../../gn:default_deps",
-    "../../../include/perfetto/profiling:normalize",
-    "../../base",
-  ]
-  sources = [
-    "proc_utils.cc",
-    "proc_utils.h",
-  ]
-}
-
 source_set("scoped_spinlock") {
   deps = [
     "../../../gn:default_deps",
@@ -133,14 +113,11 @@
     "../../../gn:gtest_and_gmock",
     "../../base",
   ]
-  sources = [
-    "shared_ring_buffer_unittest.cc",
-  ]
+  sources = [ "shared_ring_buffer_unittest.cc" ]
 }
 
 source_set("daemon") {
   deps = [
-    ":proc_utils",
     ":ring_buffer",
     ":scoped_spinlock",
     ":wire_protocol",
@@ -148,8 +125,13 @@
     "../../../protos/perfetto/config/profiling:cpp",
     "../../base",
     "../../base:unix_socket",
-    "../../tracing",
-    "../../tracing:ipc",
+    "../../tracing/core",
+    "../../tracing/ipc/producer",
+    "../common:callstack_trie",
+    "../common:interner",
+    "../common:interning_output",
+    "../common:proc_utils",
+    "../common:unwind_support",
   ]
   public_deps = [
     "../../../gn:libunwindstack",
@@ -165,7 +147,6 @@
     "bookkeeping_dump.h",
     "heapprofd_producer.cc",
     "heapprofd_producer.h",
-    "interner.h",
     "java_hprof_producer.cc",
     "java_hprof_producer.h",
     "page_idle_checker.cc",
@@ -175,24 +156,20 @@
     "unwinding.cc",
     "unwinding.h",
     "unwound_messages.h",
-    "utils.cc",
-    "utils.h",
   ]
 }
 
 source_set("client") {
   deps = [
-    ":proc_utils",
     ":ring_buffer",
     ":scoped_spinlock",
     ":wire_protocol",
     "../../../gn:default_deps",
     "../../base",
     "../../base:unix_socket",
+    "../common:proc_utils",
   ]
-  public_deps = [
-    "../../../gn:libunwindstack",
-  ]
+  public_deps = [ "../../../gn:libunwindstack" ]
   sources = [
     "client.cc",
     "client.h",
@@ -205,23 +182,22 @@
   deps = [
     ":client",
     ":daemon",
-    ":proc_utils",
     ":wire_protocol",
     "../../../gn:default_deps",
     "../../../gn:gtest_and_gmock",
     "../../../gn:libunwindstack",
-    "../../../include/perfetto/profiling:normalize",
     "../../base",
     "../../base:test_support",
-    "../../tracing",
+    "../../tracing/core",
+    "../common:proc_utils",
+    "../common:unwind_support",
   ]
   sources = [
     "bookkeeping_unittest.cc",
     "client_unittest.cc",
     "heapprofd_producer_unittest.cc",
-    "interner_unittest.cc",
     "page_idle_checker_unittest.cc",
-    "proc_utils_unittest.cc",
+    "parse_smaps_unittest.cc",
     "sampler_unittest.cc",
     "system_property_unittest.cc",
     "unwinding_unittest.cc",
@@ -244,9 +220,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 +228,21 @@
 
 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",
+    "../common:unwind_support",
   ]
 }
 
 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 +252,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/bookkeeping.cc b/src/profiling/memory/bookkeeping.cc
index bd6ee97..93c9c17 100644
--- a/src/profiling/memory/bookkeeping.cc
+++ b/src/profiling/memory/bookkeeping.cc
@@ -24,6 +24,7 @@
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/scoped_file.h"
+#include "src/profiling/common/callstack_trie.h"
 
 namespace perfetto {
 namespace profiling {
@@ -34,6 +35,18 @@
                                uint64_t alloc_size,
                                uint64_t sequence_number,
                                uint64_t timestamp) {
+  std::vector<Interned<Frame>> frames;
+  frames.reserve(callstack.size());
+  for (const FrameData& loc : callstack) {
+    auto frame_it = frame_cache_.find(loc.frame.pc);
+    if (frame_it != frame_cache_.end()) {
+      frames.emplace_back(frame_it->second);
+    } else {
+      frames.emplace_back(callsites_->InternCodeLocation(loc));
+      frame_cache_.emplace(loc.frame.pc, frames.back());
+    }
+  }
+
   auto it = allocations_.find(address);
   if (it != allocations_.end()) {
     Allocation& alloc = it->second;
@@ -54,14 +67,14 @@
       }
 
       SubtractFromCallstackAllocations(alloc);
-      GlobalCallstackTrie::Node* node = callsites_->CreateCallsite(callstack);
+      GlobalCallstackTrie::Node* node = callsites_->CreateCallsite(frames);
       alloc.sample_size = sample_size;
       alloc.alloc_size = alloc_size;
       alloc.sequence_number = sequence_number;
       alloc.SetCallstackAllocations(MaybeCreateCallstackAllocations(node));
     }
   } else {
-    GlobalCallstackTrie::Node* node = callsites_->CreateCallsite(callstack);
+    GlobalCallstackTrie::Node* node = callsites_->CreateCallsite(frames);
     allocations_.emplace(address,
                          Allocation(sample_size, alloc_size, sequence_number,
                                     MaybeCreateCallstackAllocations(node)));
@@ -146,82 +159,6 @@
   return alloc.value.retain_max.max;
 }
 
-GlobalCallstackTrie::Node* GlobalCallstackTrie::GetOrCreateChild(
-    Node* self,
-    const Interned<Frame>& loc) {
-  Node* child = self->children_.Get(loc);
-  if (!child)
-    child = self->children_.Emplace(loc, ++next_callstack_id_, self);
-  return child;
-}
-
-std::vector<Interned<Frame>> GlobalCallstackTrie::BuildCallstack(
-    const Node* node) const {
-  std::vector<Interned<Frame>> res;
-  while (node != &root_) {
-    res.emplace_back(node->location_);
-    node = node->parent_;
-  }
-  return res;
-}
-
-GlobalCallstackTrie::Node* GlobalCallstackTrie::CreateCallsite(
-    const std::vector<FrameData>& callstack) {
-  Node* node = &root_;
-  for (const FrameData& loc : callstack) {
-    node = GetOrCreateChild(node, InternCodeLocation(loc));
-  }
-  return node;
-}
-
-void GlobalCallstackTrie::IncrementNode(Node* node) {
-  while (node != nullptr) {
-    node->ref_count_ += 1;
-    node = node->parent_;
-  }
-}
-
-void GlobalCallstackTrie::DecrementNode(Node* node) {
-  PERFETTO_DCHECK(node->ref_count_ >= 1);
-
-  bool delete_prev = false;
-  Node* prev = nullptr;
-  while (node != nullptr) {
-    if (delete_prev)
-      node->children_.Remove(*prev);
-    node->ref_count_ -= 1;
-    delete_prev = node->ref_count_ == 0;
-    prev = node;
-    node = node->parent_;
-  }
-}
-
-Interned<Frame> GlobalCallstackTrie::InternCodeLocation(const FrameData& loc) {
-  Mapping map(string_interner_.Intern(loc.build_id));
-  map.exact_offset = loc.frame.map_exact_offset;
-  map.start_offset = loc.frame.map_elf_start_offset;
-  map.start = loc.frame.map_start;
-  map.end = loc.frame.map_end;
-  map.load_bias = loc.frame.map_load_bias;
-  base::StringSplitter sp(loc.frame.map_name, '/');
-  while (sp.Next())
-    map.path_components.emplace_back(string_interner_.Intern(sp.cur_token()));
-
-  Frame frame(mapping_interner_.Intern(std::move(map)),
-              string_interner_.Intern(loc.frame.function_name),
-              loc.frame.rel_pc);
-
-  return frame_interner_.Intern(frame);
-}
-
-Interned<Frame> GlobalCallstackTrie::MakeRootFrame() {
-  Mapping map(string_interner_.Intern(""));
-
-  Frame frame(mapping_interner_.Intern(std::move(map)),
-              string_interner_.Intern(""), 0);
-
-  return frame_interner_.Intern(frame);
-}
 
 }  // namespace profiling
 }  // namespace perfetto
diff --git a/src/profiling/memory/bookkeeping.h b/src/profiling/memory/bookkeeping.h
index c157e07..9568d4b 100644
--- a/src/profiling/memory/bookkeeping.h
+++ b/src/profiling/memory/bookkeeping.h
@@ -18,13 +18,11 @@
 #define SRC_PROFILING_MEMORY_BOOKKEEPING_H_
 
 #include <map>
-#include <string>
 #include <vector>
 
 #include "perfetto/base/time.h"
-#include "perfetto/ext/base/lookup_set.h"
-#include "perfetto/ext/base/string_splitter.h"
-#include "src/profiling/memory/interner.h"
+#include "src/profiling/common/callstack_trie.h"
+#include "src/profiling/common/interner.h"
 #include "src/profiling/memory/unwound_messages.h"
 
 // Below is an illustration of the bookkeeping system state where
@@ -91,129 +89,6 @@
 namespace perfetto {
 namespace profiling {
 
-class HeapTracker;
-
-struct Mapping {
-  Mapping(Interned<std::string> b) : build_id(std::move(b)) {}
-
-  Interned<std::string> build_id;
-  uint64_t exact_offset = 0;
-  uint64_t start_offset = 0;
-  uint64_t start = 0;
-  uint64_t end = 0;
-  uint64_t load_bias = 0;
-  std::vector<Interned<std::string>> path_components{};
-
-  bool operator<(const Mapping& other) const {
-    return std::tie(build_id, exact_offset, start_offset, start, end, load_bias,
-                    path_components) <
-           std::tie(other.build_id, other.exact_offset, other.start_offset,
-                    other.start, other.end, other.load_bias,
-                    other.path_components);
-  }
-  bool operator==(const Mapping& other) const {
-    return std::tie(build_id, exact_offset, start_offset, start, end, load_bias,
-                    path_components) ==
-           std::tie(other.build_id, other.exact_offset, other.start_offset,
-                    other.start, other.end, other.load_bias,
-                    other.path_components);
-  }
-};
-
-struct Frame {
-  Frame(Interned<Mapping> m, Interned<std::string> fn_name, uint64_t pc)
-      : mapping(m), function_name(fn_name), rel_pc(pc) {}
-  Interned<Mapping> mapping;
-  Interned<std::string> function_name;
-  uint64_t rel_pc;
-
-  bool operator<(const Frame& other) const {
-    return std::tie(mapping, function_name, rel_pc) <
-           std::tie(other.mapping, other.function_name, other.rel_pc);
-  }
-
-  bool operator==(const Frame& other) const {
-    return std::tie(mapping, function_name, rel_pc) ==
-           std::tie(other.mapping, other.function_name, other.rel_pc);
-  }
-};
-
-// Graph of function callsites. This is shared between heap dumps for
-// different processes. Each call site is represented by a
-// GlobalCallstackTrie::Node that is owned by the parent (i.e. calling)
-// callsite. It has a pointer to its parent, which means the function
-// call-graph can be reconstructed from a GlobalCallstackTrie::Node by walking
-// down the pointers to the parents.
-class GlobalCallstackTrie {
- public:
-  // Node in a tree of function traces that resulted in an allocation. For
-  // instance, if alloc_buf is called from foo and bar, which are called from
-  // main, the tree looks as following.
-  //
-  //            alloc_buf    alloc_buf
-  //                   |      |
-  //                  foo    bar
-  //                    \    /
-  //                      main
-  //                       |
-  //                   libc_init
-  //                       |
-  //                    [root_]
-  //
-  // allocations_ will hold a map from the pointers returned from malloc to
-  // alloc_buf to the leafs of this tree.
-  class Node {
-   public:
-    // This is opaque except to GlobalCallstackTrie.
-    friend class GlobalCallstackTrie;
-
-    Node(Interned<Frame> frame) : Node(std::move(frame), 0, nullptr) {}
-    Node(Interned<Frame> frame, uint64_t id)
-        : Node(std::move(frame), id, nullptr) {}
-    Node(Interned<Frame> frame, uint64_t id, Node* parent)
-        : id_(id), parent_(parent), location_(std::move(frame)) {}
-
-    uint64_t id() const { return id_; }
-
-   private:
-    Node* GetOrCreateChild(const Interned<Frame>& loc);
-
-    uint64_t ref_count_ = 0;
-    uint64_t id_;
-    Node* const parent_;
-    const Interned<Frame> location_;
-    base::LookupSet<Node, const Interned<Frame>, &Node::location_> children_;
-  };
-
-  GlobalCallstackTrie() = default;
-  GlobalCallstackTrie(const GlobalCallstackTrie&) = delete;
-  GlobalCallstackTrie& operator=(const GlobalCallstackTrie&) = delete;
-
-  // Moving this would invalidate the back pointers to the root_ node.
-  GlobalCallstackTrie(GlobalCallstackTrie&&) = delete;
-  GlobalCallstackTrie& operator=(GlobalCallstackTrie&&) = delete;
-
-  Node* CreateCallsite(const std::vector<FrameData>& locs);
-  static void DecrementNode(Node* node);
-  static void IncrementNode(Node* node);
-
-  std::vector<Interned<Frame>> BuildCallstack(const Node* node) const;
-
- private:
-  Node* GetOrCreateChild(Node* self, const Interned<Frame>& loc);
-
-  Interned<Frame> InternCodeLocation(const FrameData& loc);
-  Interned<Frame> MakeRootFrame();
-
-  Interner<std::string> string_interner_;
-  Interner<Mapping> mapping_interner_;
-  Interner<Frame> frame_interner_;
-
-  uint64_t next_callstack_id_ = 0;
-
-  Node root_{MakeRootFrame(), ++next_callstack_id_};
-};
-
 // Snapshot for memory allocations of a particular process. Shares callsites
 // with other processes.
 class HeapTracker {
@@ -270,8 +145,12 @@
       auto& it = it_and_alloc.first;
       uint64_t allocated = it_and_alloc.second;
       const CallstackAllocations& alloc = it->second;
-      if (alloc.allocs == 0 && alloc.allocation_count == allocated)
+      if (alloc.allocs == 0 && alloc.allocation_count == allocated) {
+        // TODO(fmayer): We could probably be smarter than throw away
+        // our whole frames cache.
+        ClearFrameCache();
         callstack_allocations_.erase(it);
+      }
     }
     dead_callstack_allocations_.clear();
 
@@ -300,6 +179,8 @@
     RecordOperation(sequence_number, {address, timestamp});
   }
 
+  void ClearFrameCache() { frame_cache_.clear(); }
+
   uint64_t committed_timestamp() { return committed_timestamp_; }
   uint64_t max_timestamp() { return max_timestamp_; }
 
@@ -455,41 +336,13 @@
   uint64_t current_unfreed_ = 0;
   uint64_t max_unfreed_ = 0;
   uint64_t max_timestamp_ = 0;
+
+  // We index by abspc, which is unique as long as the maps do not change.
+  // This is why we ClearFrameCache after we reparsed maps.
+  std::unordered_map<uint64_t /* abs pc */, Interned<Frame>> frame_cache_;
 };
 
 }  // namespace profiling
 }  // namespace perfetto
 
-namespace std {
-template <>
-struct hash<::perfetto::profiling::Mapping> {
-  using argument_type = ::perfetto::profiling::Mapping;
-  using result_type = size_t;
-  result_type operator()(const argument_type& mapping) {
-    size_t h =
-        std::hash<::perfetto::profiling::InternID>{}(mapping.build_id.id());
-    h ^= std::hash<uint64_t>{}(mapping.exact_offset);
-    h ^= std::hash<uint64_t>{}(mapping.start_offset);
-    h ^= std::hash<uint64_t>{}(mapping.start);
-    h ^= std::hash<uint64_t>{}(mapping.end);
-    h ^= std::hash<uint64_t>{}(mapping.load_bias);
-    for (const auto& path : mapping.path_components)
-      h ^= std::hash<uint64_t>{}(path.id());
-    return h;
-  }
-};
-
-template <>
-struct hash<::perfetto::profiling::Frame> {
-  using argument_type = ::perfetto::profiling::Frame;
-  using result_type = size_t;
-  result_type operator()(const argument_type& frame) {
-    size_t h = std::hash<::perfetto::profiling::InternID>{}(frame.mapping.id());
-    h ^= std::hash<::perfetto::profiling::InternID>{}(frame.function_name.id());
-    h ^= std::hash<uint64_t>{}(frame.rel_pc);
-    return h;
-  }
-};
-}  // namespace std
-
 #endif  // SRC_PROFILING_MEMORY_BOOKKEEPING_H_
diff --git a/src/profiling/memory/bookkeeping_dump.cc b/src/profiling/memory/bookkeeping_dump.cc
index 6f72074..514424b 100644
--- a/src/profiling/memory/bookkeeping_dump.cc
+++ b/src/profiling/memory/bookkeeping_dump.cc
@@ -19,7 +19,6 @@
 namespace perfetto {
 namespace profiling {
 namespace {
-using ::perfetto::protos::pbzero::Callstack;
 using ::perfetto::protos::pbzero::ProfilePacket;
 // This needs to be lower than the maximum acceptable chunk size, because this
 // is checked *before* writing another submessage. We conservatively assume
@@ -28,113 +27,31 @@
 uint32_t kPacketSizeThreshold = 400000;
 }  // namespace
 
-void WriteFixedInternings(TraceWriter* trace_writer) {
-  constexpr const uint8_t kEmptyString[] = "";
-  // Explicitly reserve intern ID 0 for the empty string, so unset string
-  // fields get mapped to this.
-  auto packet = trace_writer->NewTracePacket();
-  auto* interned_data = packet->set_interned_data();
-  auto interned_string = interned_data->add_build_ids();
-  interned_string->set_iid(0);
-  interned_string->set_str(kEmptyString, 0);
-
-  interned_string = interned_data->add_mapping_paths();
-  interned_string->set_iid(0);
-  interned_string->set_str(kEmptyString, 0);
-
-  interned_string = interned_data->add_function_names();
-  interned_string->set_iid(0);
-  interned_string->set_str(kEmptyString, 0);
-
-  packet->set_incremental_state_cleared(true);
-}
-
 void DumpState::WriteMap(const Interned<Mapping> map) {
-  auto map_it_and_inserted = intern_state_->dumped_mappings_.emplace(map.id());
-  if (map_it_and_inserted.second) {
-    for (const Interned<std::string>& str : map->path_components)
-      WriteMappingPathString(str);
-
-    WriteBuildIDString(map->build_id);
-
-    auto mapping = GetCurrentInternedData()->add_mappings();
-    mapping->set_iid(map.id());
-    mapping->set_exact_offset(map->exact_offset);
-    mapping->set_start_offset(map->start_offset);
-    mapping->set_start(map->start);
-    mapping->set_end(map->end);
-    mapping->set_load_bias(map->load_bias);
-    mapping->set_build_id(map->build_id.id());
-    for (const Interned<std::string>& str : map->path_components)
-      mapping->add_path_string_ids(str.id());
-  }
+  intern_state_->WriteMap(map, GetCurrentInternedData());
 }
 
 void DumpState::WriteFrame(Interned<Frame> frame) {
-  WriteMap(frame->mapping);
-  WriteFunctionNameString(frame->function_name);
-  bool inserted;
-  std::tie(std::ignore, inserted) =
-      intern_state_->dumped_frames_.emplace(frame.id());
-  if (inserted) {
-    auto frame_proto = GetCurrentInternedData()->add_frames();
-    frame_proto->set_iid(frame.id());
-    frame_proto->set_function_name_id(frame->function_name.id());
-    frame_proto->set_mapping_id(frame->mapping.id());
-    frame_proto->set_rel_pc(frame->rel_pc);
-  }
+  intern_state_->WriteFrame(frame, GetCurrentInternedData());
 }
 
 void DumpState::WriteBuildIDString(const Interned<std::string>& str) {
-  auto it_and_inserted = intern_state_->dumped_strings_.emplace(str.id(), 0);
-  auto it = it_and_inserted.first;
-  // This is for the rare case that the same string is used as two different
-  // types (e.g. a function name that matches a path segment). In that case
-  // we need to emit the string as all of its types.
-  if ((it->second & kDumpedBuildID) == 0) {
-    auto interned_string = GetCurrentInternedData()->add_build_ids();
-    interned_string->set_iid(str.id());
-    interned_string->set_str(reinterpret_cast<const uint8_t*>(str->c_str()),
-                             str->size());
-    it->second |= kDumpedBuildID;
-  }
+  intern_state_->WriteBuildIDString(str, GetCurrentInternedData());
 }
 
 void DumpState::WriteMappingPathString(const Interned<std::string>& str) {
-  auto it_and_inserted = intern_state_->dumped_strings_.emplace(str.id(), 0);
-  auto it = it_and_inserted.first;
-  // This is for the rare case that the same string is used as two different
-  // types (e.g. a function name that matches a path segment). In that case
-  // we need to emit the string as all of its types.
-  if ((it->second & kDumpedMappingPath) == 0) {
-    auto interned_string = GetCurrentInternedData()->add_mapping_paths();
-    interned_string->set_iid(str.id());
-    interned_string->set_str(reinterpret_cast<const uint8_t*>(str->c_str()),
-                             str->size());
-    it->second |= kDumpedMappingPath;
-  }
+  intern_state_->WriteMappingPathString(str, GetCurrentInternedData());
 }
 
 void DumpState::WriteFunctionNameString(const Interned<std::string>& str) {
-  auto it_and_inserted = intern_state_->dumped_strings_.emplace(str.id(), 0);
-  auto it = it_and_inserted.first;
-  // This is for the rare case that the same string is used as two different
-  // types (e.g. a function name that matches a path segment). In that case
-  // we need to emit the string as all of its types.
-  if ((it->second & kDumpedFunctionName) == 0) {
-    auto interned_string = GetCurrentInternedData()->add_function_names();
-    interned_string->set_iid(str.id());
-    interned_string->set_str(reinterpret_cast<const uint8_t*>(str->c_str()),
-                             str->size());
-    it->second |= kDumpedFunctionName;
-  }
+  intern_state_->WriteFunctionNameString(str, GetCurrentInternedData());
 }
 
 void DumpState::WriteAllocation(const HeapTracker::CallstackAllocations& alloc,
                                 bool dump_at_max_mode) {
-  if (intern_state_->dumped_callstacks_.find(alloc.node->id()) ==
-      intern_state_->dumped_callstacks_.end())
+  if (intern_state_->IsCallstackNew(alloc.node->id())) {
     callstacks_to_dump_.emplace(alloc.node);
+  }
 
   auto* heap_samples = GetCurrentProcessHeapSamples();
   ProfilePacket::HeapSample* sample = heap_samples->add_samples();
@@ -165,17 +82,7 @@
   if (current_trace_packet_)
     current_profile_packet_->set_continued(true);
   for (GlobalCallstackTrie::Node* node : callstacks_to_dump_) {
-    // There need to be two separate loops over built_callstack because
-    // protozero cannot interleave different messages.
-    auto built_callstack = callsites->BuildCallstack(node);
-    for (const Interned<Frame>& frame : built_callstack)
-      WriteFrame(frame);
-    Callstack* callstack = GetCurrentInternedData()->add_callstacks();
-    callstack->set_iid(node->id());
-    for (const Interned<Frame>& frame : built_callstack)
-      callstack->add_frame_ids(frame.id());
-
-    intern_state_->dumped_callstacks_.emplace(node->id());
+    intern_state_->WriteCallstack(node, callsites, GetCurrentInternedData());
   }
   MakeProfilePacket();
 }
diff --git a/src/profiling/memory/bookkeeping_dump.h b/src/profiling/memory/bookkeeping_dump.h
index 517d02c..bf19133 100644
--- a/src/profiling/memory/bookkeeping_dump.h
+++ b/src/profiling/memory/bookkeeping_dump.h
@@ -29,37 +29,20 @@
 
 #include "perfetto/ext/tracing/core/trace_writer.h"
 
+#include "src/profiling/common/interner.h"
+#include "src/profiling/common/interning_output.h"
 #include "src/profiling/memory/bookkeeping.h"
-#include "src/profiling/memory/interner.h"
 
 namespace perfetto {
 namespace profiling {
 
-void WriteFixedInternings(TraceWriter* trace_writer);
-
-constexpr int kDumpedBuildID = 1 << 0;
-constexpr int kDumpedMappingPath = 1 << 1;
-constexpr int kDumpedFunctionName = 1 << 2;
-
 class DumpState {
  public:
-  class InternState {
-   private:
-    friend class DumpState;
-
-    std::map<InternID, int> dumped_strings_;
-    std::set<InternID> dumped_frames_;
-    std::set<InternID> dumped_mappings_;
-    std::set<uint64_t> dumped_callstacks_;
-
-    uint64_t next_index_ = 0;
-  };
-
   DumpState(
       TraceWriter* trace_writer,
       std::function<void(protos::pbzero::ProfilePacket::ProcessHeapSamples*)>
           process_fill_header,
-      InternState* intern_state)
+      InterningOutputTracker* intern_state)
       : trace_writer_(trace_writer),
         intern_state_(intern_state),
         current_process_fill_header_(std::move(process_fill_header)) {
@@ -103,7 +86,8 @@
     MakeTracePacket();
 
     current_profile_packet_ = current_trace_packet_->set_profile_packet();
-    current_profile_packet_->set_index(intern_state_->next_index_++);
+    uint64_t* next_index = intern_state_->HeapprofdNextIndexMutable();
+    current_profile_packet_->set_index((*next_index)++);
   }
 
   uint64_t currently_written() {
@@ -117,7 +101,7 @@
   std::set<GlobalCallstackTrie::Node*> callstacks_to_dump_;
 
   TraceWriter* trace_writer_;
-  InternState* intern_state_;
+  InterningOutputTracker* intern_state_;
 
   protos::pbzero::ProfilePacket* current_profile_packet_ = nullptr;
   protos::pbzero::InternedData* current_interned_data_ = nullptr;
diff --git a/src/profiling/memory/bookkeeping_unittest.cc b/src/profiling/memory/bookkeeping_unittest.cc
index 940e23c..b3d6a4e 100644
--- a/src/profiling/memory/bookkeeping_unittest.cc
+++ b/src/profiling/memory/bookkeeping_unittest.cc
@@ -31,10 +31,12 @@
   unwindstack::FrameData data{};
   data.function_name = "fun1";
   data.map_name = "map1";
+  data.pc = 1;
   res.emplace_back(std::move(data), "dummy_buildid");
   data = {};
   data.function_name = "fun2";
   data.map_name = "map2";
+  data.pc = 2;
   res.emplace_back(std::move(data), "dummy_buildid");
   return res;
 }
@@ -44,10 +46,12 @@
   unwindstack::FrameData data{};
   data.function_name = "fun1";
   data.map_name = "map1";
+  data.pc = 1;
   res.emplace_back(std::move(data), "dummy_buildid");
   data = {};
   data.function_name = "fun3";
   data.map_name = "map3";
+  data.pc = 3;
   res.emplace_back(std::move(data), "dummy_buildid");
   return res;
 }
diff --git a/src/profiling/memory/client.cc b/src/profiling/memory/client.cc
index ed65372..55abec4 100644
--- a/src/profiling/memory/client.cc
+++ b/src/profiling/memory/client.cc
@@ -56,8 +56,12 @@
   return getpid() == base::GetThreadId();
 }
 
-// TODO(b/117203899): Remove this after making bionic implementation safe to
-// use.
+// The implementation of pthread_getattr_np for the main thread uses malloc,
+// so we cannot use it in GetStackBase, which we use inside of RecordMalloc
+// (which is called from malloc). We would re-enter malloc if we used it.
+//
+// This is why we find the stack base for the main-thread when constructing
+// the client and remember it.
 char* FindMainThreadStack() {
   base::ScopedFstream maps(fopen("/proc/self/maps", "r"));
   if (!maps) {
@@ -170,7 +174,7 @@
 
   base::ScopedFile page_idle(base::OpenFile("/proc/self/page_idle", O_RDWR));
   if (!page_idle) {
-    PERFETTO_LOG("Failed to open /proc/self/page_idle. Continuing.");
+    PERFETTO_DLOG("Failed to open /proc/self/page_idle. Continuing.");
     num_send_fds = kHandshakeSize - 1;
   }
 
@@ -273,6 +277,26 @@
   return GetThreadStackBase();
 }
 
+bool Client::IsPostFork() {
+  if (PERFETTO_UNLIKELY(getpid() != pid_at_creation_)) {
+    // Only print the message once, even if we do not shut down the client.
+    if (!detected_fork_) {
+      detected_fork_ = true;
+      postfork_return_value_ = client_config_.disable_fork_teardown;
+      const char* action =
+          postfork_return_value_ ? "Not shutting down" : "Shutting down";
+      const char* force =
+          postfork_return_value_ ? "fork teardown disabled" : "";
+      PERFETTO_LOG(
+          "Detected post-fork child situation. Not profiling the child. "
+          "%s client (%s)",
+          action, force);
+    }
+    return true;
+  }
+  return false;
+}
+
 // The stack grows towards numerically smaller addresses, so the stack layout
 // of main calling malloc is as follows.
 //
@@ -288,10 +312,8 @@
 bool Client::RecordMalloc(uint64_t sample_size,
                           uint64_t alloc_size,
                           uint64_t alloc_address) {
-  if (PERFETTO_UNLIKELY(getpid() != pid_at_creation_)) {
-    PERFETTO_LOG(
-        "Detected post-fork child situation. Not profiling the child.");
-    return false;
+  if (PERFETTO_UNLIKELY(IsPostFork())) {
+    return postfork_return_value_;
   }
 
   AllocMetadata metadata;
@@ -371,9 +393,8 @@
 }
 
 bool Client::FlushFreesLocked() {
-  if (PERFETTO_UNLIKELY(getpid() != pid_at_creation_)) {
-    PERFETTO_LOG("Detected post-fork child situation, stopping profiling.");
-    return false;
+  if (PERFETTO_UNLIKELY(IsPostFork())) {
+    return postfork_return_value_;
   }
 
   WireMessage msg = {};
diff --git a/src/profiling/memory/client.h b/src/profiling/memory/client.h
index d2d9723..c6e581e 100644
--- a/src/profiling/memory/client.h
+++ b/src/profiling/memory/client.h
@@ -25,6 +25,7 @@
 #include <mutex>
 #include <vector>
 
+#include "perfetto/base/compiler.h"
 #include "perfetto/ext/base/unix_socket.h"
 #include "src/profiling/memory/sampler.h"
 #include "src/profiling/memory/shared_ring_buffer.h"
@@ -67,10 +68,10 @@
 
   bool RecordMalloc(uint64_t sample_size,
                     uint64_t alloc_size,
-                    uint64_t alloc_address);
+                    uint64_t alloc_address) PERFETTO_WARN_UNUSED_RESULT;
 
   // Add address to buffer of deallocations. Flushes the buffer if necessary.
-  bool RecordFree(uint64_t alloc_address);
+  bool RecordFree(uint64_t alloc_address) PERFETTO_WARN_UNUSED_RESULT;
 
   // Returns the number of bytes to assign to an allocation with the given
   // |alloc_size|, based on the current sampling rate. A return value of zero
@@ -98,9 +99,12 @@
  private:
   const char* GetStackBase();
   // Flush the contents of free_batch_. Must hold free_batch_lock_.
-  bool FlushFreesLocked();
-  bool SendControlSocketByte();
-  bool SendWireMessageWithRetriesIfBlocking(const WireMessage&);
+  bool FlushFreesLocked() PERFETTO_WARN_UNUSED_RESULT;
+  bool SendControlSocketByte() PERFETTO_WARN_UNUSED_RESULT;
+  bool SendWireMessageWithRetriesIfBlocking(const WireMessage&)
+      PERFETTO_WARN_UNUSED_RESULT;
+
+  bool IsPostFork();
 
   // This is only valid for non-blocking sockets. This is when
   // client_config_.block_client is true.
@@ -126,6 +130,8 @@
   // it'll proceed to write to the same shared buffer & control socket (with
   // duplicate sequence ids).
   const pid_t pid_at_creation_;
+  bool detected_fork_ = false;
+  bool postfork_return_value_ = false;
 };
 
 }  // namespace profiling
diff --git a/src/profiling/memory/heapprofd_end_to_end_test.cc b/src/profiling/memory/heapprofd_end_to_end_test.cc
index 96d4f50..20c01e7 100644
--- a/src/profiling/memory/heapprofd_end_to_end_test.cc
+++ b/src/profiling/memory/heapprofd_end_to_end_test.cc
@@ -15,11 +15,14 @@
  */
 
 #include <fcntl.h>
+#include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <unistd.h>
 
 #include "perfetto/base/build_config.h"
 #include "perfetto/ext/base/pipe.h"
+#include "perfetto/ext/base/subprocess.h"
 #include "perfetto/ext/tracing/ipc/default_socket.h"
 #include "src/base/test/test_task_runner.h"
 #include "src/profiling/memory/heapprofd_producer.h"
@@ -124,19 +127,11 @@
   }
 }
 
-pid_t ForkContinuousMalloc(size_t bytes) {
-  // Make sure forked process does not get reparented to init.
-  setsid();
-  pid_t pid = fork();
-  switch (pid) {
-    case -1:
-      PERFETTO_FATAL("Failed to fork.");
-    case 0:
-      ContinuousMalloc(bytes);
-    default:
-      break;
-  }
-  return pid;
+base::Subprocess ForkContinuousMalloc(size_t bytes) {
+  base::Subprocess proc;
+  proc.args.entrypoint_for_testing = [bytes] { ContinuousMalloc(bytes); };
+  proc.Start();
+  return proc;
 }
 
 void __attribute__((constructor)) RunContinuousMalloc() {
@@ -307,10 +302,18 @@
   }
 };
 
+// This checks that the child is still running (to ensure it didn't crash
+// unxpectedly) and then kills it.
+void KillAssertRunning(base::Subprocess* child) {
+  ASSERT_EQ(child->Poll(), base::Subprocess::kRunning);
+  child->KillAndWaitForTermination();
+}
+
 TEST_P(HeapprofdEndToEnd, Smoke) {
   constexpr size_t kAllocSize = 1024;
 
-  pid_t pid = ForkContinuousMalloc(kAllocSize);
+  base::Subprocess child = ForkContinuousMalloc(kAllocSize);
+  const auto pid = child.pid();
 
   TraceConfig trace_config;
   trace_config.add_buffers()->set_size_kb(10 * 1024);
@@ -336,16 +339,17 @@
   ValidateOnlyPID(helper.get(), static_cast<uint64_t>(pid));
   ValidateSampleSizes(helper.get(), static_cast<uint64_t>(pid), kAllocSize);
 
-  PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
-  PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
+  KillAssertRunning(&child);
 }
 
 TEST_P(HeapprofdEndToEnd, TwoProcesses) {
   constexpr size_t kAllocSize = 1024;
   constexpr size_t kAllocSize2 = 7;
 
-  pid_t pid = ForkContinuousMalloc(kAllocSize);
-  pid_t pid2 = ForkContinuousMalloc(kAllocSize2);
+  base::Subprocess child = ForkContinuousMalloc(kAllocSize);
+  base::Subprocess child2 = ForkContinuousMalloc(kAllocSize2);
+  const auto pid = child.pid();
+  const auto pid2 = child2.pid();
 
   TraceConfig trace_config;
   trace_config.add_buffers()->set_size_kb(10 * 1024);
@@ -370,16 +374,15 @@
   ValidateHasSamples(helper.get(), static_cast<uint64_t>(pid2));
   ValidateSampleSizes(helper.get(), static_cast<uint64_t>(pid2), kAllocSize2);
 
-  PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
-  PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
-  PERFETTO_CHECK(kill(pid2, SIGKILL) == 0);
-  PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid2, nullptr, 0)) == pid2);
+  KillAssertRunning(&child);
+  KillAssertRunning(&child2);
 }
 
 TEST_P(HeapprofdEndToEnd, FinalFlush) {
   constexpr size_t kAllocSize = 1024;
 
-  pid_t pid = ForkContinuousMalloc(kAllocSize);
+  base::Subprocess child = ForkContinuousMalloc(kAllocSize);
+  const auto pid = child.pid();
 
   TraceConfig trace_config;
   trace_config.add_buffers()->set_size_kb(10 * 1024);
@@ -402,8 +405,7 @@
   ValidateOnlyPID(helper.get(), static_cast<uint64_t>(pid));
   ValidateSampleSizes(helper.get(), static_cast<uint64_t>(pid), kAllocSize);
 
-  PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
-  PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
+  KillAssertRunning(&child);
 }
 
 TEST_P(HeapprofdEndToEnd, NativeStartup) {
@@ -432,33 +434,19 @@
   // has received the trace config.
   sleep(1);
 
-  // Make sure the forked process does not get reparented to init.
-  setsid();
-  pid_t pid = fork();
-  switch (pid) {
-    case -1:
-      PERFETTO_FATAL("Failed to fork.");
-    case 0: {
-      const char* envp[] = {"HEAPPROFD_TESTING_RUN_MALLOC=1", nullptr};
-      int null = open("/dev/null", O_RDWR);
-      dup2(null, STDIN_FILENO);
-      dup2(null, STDOUT_FILENO);
-      dup2(null, STDERR_FILENO);
-      PERFETTO_CHECK(execle("/proc/self/exe", "heapprofd_continuous_malloc",
-                            nullptr, envp) == 0);
-      break;
-    }
-    default:
-      break;
-  }
+  base::Subprocess child({"/proc/self/exe"});
+  child.args.argv0_override = "heapprofd_continuous_malloc";
+  child.args.stdout_mode = base::Subprocess::kDevNull;
+  child.args.stderr_mode = base::Subprocess::kDevNull;
+  child.args.env.push_back("HEAPPROFD_TESTING_RUN_MALLOC=1");
+  child.Start();
 
   helper->WaitForTracingDisabled(kTracingDisabledTimeoutMs);
 
   helper->ReadData();
   helper->WaitForReadData(0, kWaitForReadDataTimeoutMs);
 
-  PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
-  PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
+  KillAssertRunning(&child);
 
   const auto& packets = helper->trace();
   ASSERT_GT(packets.size(), 0u);
@@ -472,7 +460,7 @@
       const auto& dumps = packet.profile_packet().process_dumps();
       ASSERT_EQ(dumps.size(), 1u);
       const protos::gen::ProfilePacket_ProcessHeapSamples& dump = dumps[0];
-      EXPECT_EQ(static_cast<pid_t>(dump.pid()), pid);
+      EXPECT_EQ(static_cast<pid_t>(dump.pid()), child.pid());
       profile_packets++;
       for (const auto& sample : dump.samples()) {
         samples++;
@@ -513,33 +501,19 @@
   // has received the trace config.
   sleep(1);
 
-  // Make sure the forked process does not get reparented to init.
-  setsid();
-  pid_t pid = fork();
-  switch (pid) {
-    case -1:
-      PERFETTO_FATAL("Failed to fork.");
-    case 0: {
-      const char* envp[] = {"HEAPPROFD_TESTING_RUN_MALLOC=1", nullptr};
-      int null = open("/dev/null", O_RDWR);
-      dup2(null, STDIN_FILENO);
-      dup2(null, STDOUT_FILENO);
-      dup2(null, STDERR_FILENO);
-      PERFETTO_CHECK(execle("/proc/self/exe", "heapprofd_continuous_malloc",
-                            nullptr, envp) == 0);
-      break;
-    }
-    default:
-      break;
-  }
+  base::Subprocess child({"/proc/self/exe"});
+  child.args.argv0_override = "heapprofd_continuous_malloc";
+  child.args.stdout_mode = base::Subprocess::kDevNull;
+  child.args.stderr_mode = base::Subprocess::kDevNull;
+  child.args.env.push_back("HEAPPROFD_TESTING_RUN_MALLOC=1");
+  child.Start();
 
   helper->WaitForTracingDisabled(kTracingDisabledTimeoutMs);
 
   helper->ReadData();
   helper->WaitForReadData(0, kWaitForReadDataTimeoutMs);
 
-  PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
-  PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
+  KillAssertRunning(&child);
 
   const auto& packets = helper->trace();
   ASSERT_GT(packets.size(), 0u);
@@ -553,7 +527,7 @@
       const auto& dumps = packet.profile_packet().process_dumps();
       ASSERT_EQ(dumps.size(), 1u);
       const protos::gen::ProfilePacket_ProcessHeapSamples& dump = dumps[0];
-      EXPECT_EQ(static_cast<pid_t>(dump.pid()), pid);
+      EXPECT_EQ(static_cast<pid_t>(dump.pid()), child.pid());
       profile_packets++;
       for (const auto& sample : dump.samples()) {
         samples++;
@@ -585,25 +559,12 @@
   heapprofd_config.set_all(false);
   ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
 
-  // Make sure the forked process does not get reparented to init.
-  setsid();
-  pid_t pid = fork();
-  switch (pid) {
-    case -1:
-      PERFETTO_FATAL("Failed to fork.");
-    case 0: {
-      const char* envp[] = {"HEAPPROFD_TESTING_RUN_MALLOC=1", nullptr};
-      int null = open("/dev/null", O_RDWR);
-      dup2(null, STDIN_FILENO);
-      dup2(null, STDOUT_FILENO);
-      dup2(null, STDERR_FILENO);
-      PERFETTO_CHECK(execle("/proc/self/exe", "heapprofd_continuous_malloc",
-                            nullptr, envp) == 0);
-      break;
-    }
-    default:
-      break;
-  }
+  base::Subprocess child({"/proc/self/exe"});
+  child.args.argv0_override = "heapprofd_continuous_malloc";
+  child.args.stdout_mode = base::Subprocess::kDevNull;
+  child.args.stderr_mode = base::Subprocess::kDevNull;
+  child.args.env.push_back("HEAPPROFD_TESTING_RUN_MALLOC=1");
+  child.Start();
 
   // Wait to make sure process is fully initialized, so we do not accidentally
   // match it by the startup logic.
@@ -615,8 +576,7 @@
   helper->ReadData();
   helper->WaitForReadData(0, kWaitForReadDataTimeoutMs);
 
-  PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
-  PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
+  KillAssertRunning(&child);
 
   const auto& packets = helper->trace();
   ASSERT_GT(packets.size(), 0u);
@@ -630,7 +590,7 @@
       const auto& dumps = packet.profile_packet().process_dumps();
       ASSERT_EQ(dumps.size(), 1u);
       const protos::gen::ProfilePacket_ProcessHeapSamples& dump = dumps[0];
-      EXPECT_EQ(static_cast<pid_t>(dump.pid()), pid);
+      EXPECT_EQ(static_cast<pid_t>(dump.pid()), child.pid());
       profile_packets++;
       for (const auto& sample : dump.samples()) {
         samples++;
@@ -663,24 +623,12 @@
   ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
 
   // Make sure the forked process does not get reparented to init.
-  setsid();
-  pid_t pid = fork();
-  switch (pid) {
-    case -1:
-      PERFETTO_FATAL("Failed to fork.");
-    case 0: {
-      const char* envp[] = {"HEAPPROFD_TESTING_RUN_MALLOC=1", nullptr};
-      int null = open("/dev/null", O_RDWR);
-      dup2(null, STDIN_FILENO);
-      dup2(null, STDOUT_FILENO);
-      dup2(null, STDERR_FILENO);
-      PERFETTO_CHECK(execle("/proc/self/exe", "heapprofd_continuous_malloc",
-                            nullptr, envp) == 0);
-      break;
-    }
-    default:
-      break;
-  }
+  base::Subprocess child({"/proc/self/exe"});
+  child.args.argv0_override = "heapprofd_continuous_malloc";
+  child.args.stdout_mode = base::Subprocess::kDevNull;
+  child.args.stderr_mode = base::Subprocess::kDevNull;
+  child.args.env.push_back("HEAPPROFD_TESTING_RUN_MALLOC=1");
+  child.Start();
 
   // Wait to make sure process is fully initialized, so we do not accidentally
   // match it by the startup logic.
@@ -692,8 +640,7 @@
   helper->ReadData();
   helper->WaitForReadData(0, kWaitForReadDataTimeoutMs);
 
-  PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
-  PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
+  KillAssertRunning(&child);
 
   const auto& packets = helper->trace();
   ASSERT_GT(packets.size(), 0u);
@@ -707,7 +654,7 @@
       const auto& dumps = packet.profile_packet().process_dumps();
       ASSERT_EQ(dumps.size(), 1u);
       const protos::gen::ProfilePacket_ProcessHeapSamples& dump = dumps[0];
-      EXPECT_EQ(static_cast<pid_t>(dump.pid()), pid);
+      EXPECT_EQ(static_cast<pid_t>(dump.pid()), child.pid());
       profile_packets++;
       for (const auto& sample : dump.samples()) {
         samples++;
@@ -729,34 +676,35 @@
   base::Pipe signal_pipe = base::Pipe::Create(base::Pipe::kBothNonBlock);
   base::Pipe ack_pipe = base::Pipe::Create(base::Pipe::kBothBlock);
 
-  setsid();
-  pid_t pid = fork();
-  switch (pid) {
-    case -1:
-      PERFETTO_FATAL("Failed to fork.");
-    case 0: {
-      size_t bytes = kFirstIterationBytes;
-      signal_pipe.wr.reset();
-      ack_pipe.rd.reset();
-      for (;;) {
-        AllocateAndFree(bytes);
-        char buf[1];
-        if (bool(signal_pipe.rd) &&
-            read(*signal_pipe.rd, buf, sizeof(buf)) == 0) {
-          // make sure the client has noticed that the session has stopped
-          AllocateAndFree(bytes);
+  base::Subprocess child;
+  int signal_pipe_rd = *signal_pipe.rd;
+  int ack_pipe_wr = *ack_pipe.wr;
+  child.args.preserve_fds.push_back(signal_pipe_rd);
+  child.args.preserve_fds.push_back(ack_pipe_wr);
+  child.args.entrypoint_for_testing = [signal_pipe_rd, ack_pipe_wr] {
+    // The Subprocess harness takes care of closing all the unused pipe ends.
+    size_t bytes = kFirstIterationBytes;
+    bool signalled = false;
+    for (;;) {
+      AllocateAndFree(bytes);
+      char buf[1];
+      if (!signalled && read(signal_pipe_rd, buf, sizeof(buf)) == 1) {
+        signalled = true;
+        close(signal_pipe_rd);
 
-          bytes = kSecondIterationBytes;
-          signal_pipe.rd.reset();
-          ack_pipe.wr.reset();
-        }
-        usleep(10 * kMsToUs);
+        // make sure the client has noticed that the session has stopped
+        AllocateAndFree(bytes);
+
+        bytes = kSecondIterationBytes;
+        PERFETTO_CHECK(PERFETTO_EINTR(write(ack_pipe_wr, "1", 1)) == 1);
+        close(ack_pipe_wr);
       }
-      PERFETTO_FATAL("Should be unreachable");
+      usleep(10 * kMsToUs);
     }
-    default:
-      break;
-  }
+    PERFETTO_FATAL("Should be unreachable");
+  };
+  child.Start();
+  auto pid = child.pid();
 
   signal_pipe.rd.reset();
   ack_pipe.wr.reset();
@@ -783,9 +731,10 @@
   ValidateSampleSizes(helper.get(), static_cast<uint64_t>(pid),
                       kFirstIterationBytes);
 
+  PERFETTO_CHECK(PERFETTO_EINTR(write(*signal_pipe.wr, "1", 1)) == 1);
   signal_pipe.wr.reset();
   char buf[1];
-  ASSERT_EQ(read(*ack_pipe.rd, buf, sizeof(buf)), 0);
+  ASSERT_EQ(PERFETTO_EINTR(read(*ack_pipe.rd, buf, sizeof(buf))), 1);
   ack_pipe.rd.reset();
 
   // A brief sleep to allow the client to notice that the profiling session is
@@ -800,14 +749,14 @@
   ValidateSampleSizes(helper.get(), static_cast<uint64_t>(pid),
                       kSecondIterationBytes);
 
-  PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
-  PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
+  KillAssertRunning(&child);
 }
 
 TEST_P(HeapprofdEndToEnd, ConcurrentSession) {
   constexpr size_t kAllocSize = 1024;
 
-  pid_t pid = ForkContinuousMalloc(kAllocSize);
+  base::Subprocess child = ForkContinuousMalloc(kAllocSize);
+  const auto pid = child.pid();
 
   TraceConfig trace_config;
   trace_config.add_buffers()->set_size_kb(10 * 1024);
@@ -851,21 +800,20 @@
   ValidateRejectedConcurrent(helper_concurrent.get(),
                              static_cast<uint64_t>(pid), true);
 
-  PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
-  PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
+  KillAssertRunning(&child);
 }
 
-// TODO(rsavitski): fold exit status assertions into existing tests where
-// possible.
 TEST_P(HeapprofdEndToEnd, NativeProfilingActiveAtProcessExit) {
   constexpr uint64_t kTestAllocSize = 128;
   base::Pipe start_pipe = base::Pipe::Create(base::Pipe::kBothBlock);
+  base::Subprocess child;
+  int start_pipe_wr = *start_pipe.wr;
+  child.args.preserve_fds.push_back(start_pipe_wr);
+  child.args.entrypoint_for_testing = [start_pipe_wr] {
+    PERFETTO_CHECK(PERFETTO_EINTR(write(start_pipe_wr, "1", 1)) == 1);
+    PERFETTO_CHECK(close(start_pipe_wr) == 0 || errno == EINTR);
 
-  setsid();
-  pid_t pid = fork();
-  if (pid == 0) {  // child
-    start_pipe.rd.reset();
-    start_pipe.wr.reset();
+    // The subprocess harness will take care of closing
     for (int i = 0; i < 200; i++) {
       // malloc and leak, otherwise the free batching will cause us to filter
       // out the allocations (as we don't see the interleaved frees).
@@ -875,10 +823,9 @@
       }
       usleep(10 * kMsToUs);
     }
-    exit(0);
-  }
-
-  ASSERT_NE(pid, -1) << "Failed to fork.";
+  };
+  child.Start();
+  auto pid = child.pid();
   start_pipe.wr.reset();
 
   // Construct tracing config (without starting profiling).
@@ -898,20 +845,16 @@
 
   // Wait for child to have been scheduled at least once.
   char buf[1] = {};
-  ASSERT_EQ(PERFETTO_EINTR(read(*start_pipe.rd, buf, sizeof(buf))), 0);
+  ASSERT_EQ(PERFETTO_EINTR(read(*start_pipe.rd, buf, sizeof(buf))), 1);
   start_pipe.rd.reset();
 
   // Trace until child exits.
   helper->StartTracing(trace_config);
 
-  siginfo_t siginfo = {};
-  int wait_ret =
-      PERFETTO_EINTR(waitid(P_PID, static_cast<id_t>(pid), &siginfo, WEXITED));
-  ASSERT_FALSE(wait_ret) << "Failed to waitid.";
-
-  // Assert that the child exited successfully.
-  EXPECT_EQ(siginfo.si_code, CLD_EXITED) << "Child did not exit by itself.";
-  EXPECT_EQ(siginfo.si_status, 0) << "Child's exit status not successful.";
+  // Wait for the child and assert that it exited successfully.
+  EXPECT_TRUE(child.Wait(30000));
+  EXPECT_EQ(child.status(), base::Subprocess::kExited);
+  EXPECT_EQ(child.returncode(), 0);
 
   // Assert that we did profile the process.
   helper->FlushAndWait(2000);
diff --git a/src/profiling/memory/heapprofd_producer.cc b/src/profiling/memory/heapprofd_producer.cc
index e077f34..283463d 100644
--- a/src/profiling/memory/heapprofd_producer.cc
+++ b/src/profiling/memory/heapprofd_producer.cc
@@ -25,8 +25,10 @@
 #include <unistd.h>
 
 #include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/thread_task_runner.h"
+#include "perfetto/ext/base/watchdog_posix.h"
 #include "perfetto/ext/tracing/core/trace_writer.h"
 #include "perfetto/ext/tracing/ipc/producer_ipc_client.h"
 #include "perfetto/tracing/core/data_source_config.h"
@@ -42,6 +44,7 @@
 
 constexpr uint32_t kInitialConnectionBackoffMs = 100;
 constexpr uint32_t kMaxConnectionBackoffMs = 30 * 1000;
+constexpr uint32_t kGuardrailIntervalMs = 30 * 1000;
 
 constexpr uint32_t kChildModeWatchdogPeriodMs = 10 * 1000;
 
@@ -124,7 +127,17 @@
       mode_(mode),
       unwinding_workers_(MakeUnwindingWorkers(this, kUnwinderThreads)),
       socket_delegate_(this),
-      weak_factory_(this) {}
+      weak_factory_(this) {
+  CheckDataSourceMemory();  // Kick off guardrail task.
+  stat_fd_.reset(open("/proc/self/stat", O_RDONLY));
+  if (!stat_fd_) {
+    PERFETTO_ELOG(
+        "Failed to open /proc/self/stat. Cannot accept profiles "
+        "with CPU guardrails.");
+  } else {
+    CheckDataSourceCpu();  // Kick off guardrail task.
+  }
+}
 
 HeapprofdProducer::~HeapprofdProducer() = default;
 
@@ -317,14 +330,18 @@
     return;
   }
 
-  std::vector<std::string> normalized_cmdlines =
+  base::Optional<std::vector<std::string>> normalized_cmdlines =
       NormalizeCmdlines(heapprofd_config.process_cmdline());
+  if (!normalized_cmdlines.has_value()) {
+    PERFETTO_ELOG("Rejecting data source due to invalid cmdline in config.");
+    return;
+  }
 
   // Child mode is only interested in the first data source matching the
   // already-connected process.
   if (mode_ == HeapprofdMode::kChild) {
     if (!ConfigTargetsProcess(heapprofd_config, target_process_,
-                              normalized_cmdlines)) {
+                              normalized_cmdlines.value())) {
       PERFETTO_DLOG("Child mode skipping setup of unrelated data source.");
       return;
     }
@@ -348,19 +365,32 @@
     }
   }
 
+  base::Optional<uint64_t> start_cputime_sec;
+  if (heapprofd_config.max_heapprofd_cpu_secs() > 0) {
+    start_cputime_sec = GetCputimeSec();
+
+    if (!start_cputime_sec) {
+      PERFETTO_ELOG("Failed to enforce CPU guardrail. Rejecting config.");
+      return;
+    }
+  }
+
   auto buffer_id = static_cast<BufferID>(ds_config.target_buffer());
   DataSource data_source(endpoint_->CreateTraceWriter(buffer_id));
   data_source.id = id;
   auto& cli_config = data_source.client_configuration;
   cli_config.interval = heapprofd_config.sampling_interval_bytes();
   cli_config.block_client = heapprofd_config.block_client();
+  cli_config.disable_fork_teardown = heapprofd_config.disable_fork_teardown();
   cli_config.block_client_timeout_us =
       heapprofd_config.block_client_timeout_us();
   data_source.config = heapprofd_config;
-  data_source.normalized_cmdlines = std::move(normalized_cmdlines);
+  data_source.normalized_cmdlines = std::move(normalized_cmdlines.value());
   data_source.stop_timeout_ms = ds_config.stop_timeout_ms();
+  data_source.start_cputime_sec = start_cputime_sec;
 
-  WriteFixedInternings(data_source.trace_writer.get());
+  InterningOutputTracker::WriteFixedInterningsPacket(
+      data_source.trace_writer.get());
   data_sources_.emplace(id, std::move(data_source));
   PERFETTO_DLOG("Set up data source.");
 
@@ -377,6 +407,20 @@
   return false;
 }
 
+base::Optional<uint64_t> HeapprofdProducer::GetCputimeSec() {
+  if (!stat_fd_) {
+    return base::nullopt;
+  }
+  lseek(stat_fd_.get(), 0, SEEK_SET);
+  base::ProcStat stat;
+  if (!ReadProcStat(stat_fd_.get(), &stat)) {
+    PERFETTO_ELOG("Failed to read stat file to enforce guardrails.");
+    return base::nullopt;
+  }
+  return (stat.utime + stat.stime) /
+         static_cast<unsigned long>(sysconf(_SC_CLK_TCK));
+}
+
 void HeapprofdProducer::SetStartupProperties(DataSource* data_source) {
   const HeapprofdConfig& heapprofd_config = data_source->config;
   if (heapprofd_config.all())
@@ -399,6 +443,9 @@
   if (!data_source->normalized_cmdlines.empty())
     FindPidsForCmdlines(data_source->normalized_cmdlines, &pids);
 
+  if (heapprofd_config.min_anonymous_memory_kb() > 0)
+    RemoveUnderAnonThreshold(heapprofd_config.min_anonymous_memory_kb(), &pids);
+
   for (auto pid_it = pids.cbegin(); pid_it != pids.cend();) {
     pid_t pid = *pid_it;
     if (IsPidProfiled(pid)) {
@@ -476,6 +523,7 @@
 void HeapprofdProducer::StopDataSource(DataSourceInstanceID id) {
   auto it = data_sources_.find(id);
   if (it == data_sources_.end()) {
+    endpoint_->NotifyDataSourceStopped(id);
     if (mode_ == HeapprofdMode::kCentral)
       PERFETTO_DFATAL_OR_ELOG(
           "Trying to stop non existing data source: %" PRIu64, id);
@@ -483,33 +531,39 @@
   }
 
   DataSource& data_source = it->second;
-  data_source.shutting_down = true;
+  data_source.was_stopped = true;
+  ShutdownDataSource(&data_source);
+}
 
+void HeapprofdProducer::ShutdownDataSource(DataSource* data_source) {
+  data_source->shutting_down = true;
   // If no processes connected, or all of them have already disconnected
   // (and have been dumped) and no PIDs have been rejected,
   // MaybeFinishDataSource can tear down the data source.
-  if (MaybeFinishDataSource(&data_source))
+  if (MaybeFinishDataSource(data_source))
     return;
 
-  if (!data_source.rejected_pids.empty()) {
-    auto trace_packet = data_source.trace_writer->NewTracePacket();
+  if (!data_source->rejected_pids.empty()) {
+    auto trace_packet = data_source->trace_writer->NewTracePacket();
     ProfilePacket* profile_packet = trace_packet->set_profile_packet();
-    for (pid_t rejected_pid : data_source.rejected_pids) {
+    for (pid_t rejected_pid : data_source->rejected_pids) {
       ProfilePacket::ProcessHeapSamples* proto =
           profile_packet->add_process_dumps();
       proto->set_pid(static_cast<uint64_t>(rejected_pid));
       proto->set_rejected_concurrent(true);
     }
     trace_packet->Finalize();
-    data_source.rejected_pids.clear();
-    if (MaybeFinishDataSource(&data_source))
+    data_source->rejected_pids.clear();
+    if (MaybeFinishDataSource(data_source))
       return;
   }
 
-  for (const auto& pid_and_process_state : data_source.process_states) {
+  for (const auto& pid_and_process_state : data_source->process_states) {
     pid_t pid = pid_and_process_state.first;
     UnwinderForPID(pid).PostDisconnectSocket(pid);
   }
+
+  auto id = data_source->id;
   auto weak_producer = weak_factory_.GetWeakPtr();
   task_runner_->PostDelayedTask(
       [weak_producer, id] {
@@ -526,7 +580,7 @@
           PERFETTO_CHECK(weak_producer->MaybeFinishDataSource(&ds));
         }
       },
-      data_source.stop_timeout_ms);
+      data_source->stop_timeout_ms);
 }
 
 void HeapprofdProducer::DoContinuousDump(DataSourceInstanceID id,
@@ -555,7 +609,8 @@
     dump_timestamp = heap_tracker.max_timestamp();
   else
     dump_timestamp = heap_tracker.committed_timestamp();
-  auto new_heapsamples = [pid, from_startup, dump_timestamp, process_state](
+  auto new_heapsamples = [pid, from_startup, dump_timestamp, process_state,
+                          data_source](
                              ProfilePacket::ProcessHeapSamples* proto) {
     proto->set_pid(static_cast<uint64_t>(pid));
     proto->set_timestamp(dump_timestamp);
@@ -563,6 +618,7 @@
     proto->set_disconnected(process_state->disconnected);
     proto->set_buffer_overran(process_state->buffer_overran);
     proto->set_buffer_corrupted(process_state->buffer_corrupted);
+    proto->set_hit_guardrail(data_source->hit_guardrail);
     auto* stats = proto->set_stats();
     stats->set_unwinding_errors(process_state->unwinding_errors);
     stats->set_heap_samples(process_state->heap_samples);
@@ -638,9 +694,6 @@
 void HeapprofdProducer::Flush(FlushRequestID flush_id,
                               const DataSourceInstanceID* ids,
                               size_t num_ids) {
-  if (num_ids == 0)
-    return;
-
   size_t& flush_in_progress = flushes_in_progress_[flush_id];
   PERFETTO_DCHECK(flush_in_progress == 0);
   flush_in_progress = num_ids;
@@ -649,6 +702,7 @@
     if (it == data_sources_.end()) {
       PERFETTO_DFATAL_OR_ELOG("Trying to flush unknown data-source %" PRIu64,
                               ids[i]);
+      flush_in_progress--;
       continue;
     }
     DataSource& data_source = it->second;
@@ -665,6 +719,10 @@
     };
     data_source.trace_writer->Flush(std::move(callback));
   }
+  if (flush_in_progress == 0) {
+    endpoint_->NotifyFlushComplete(flush_id);
+    flushes_in_progress_.erase(flush_id);
+  }
 }
 
 void HeapprofdProducer::FinishDataSourceFlush(FlushRequestID flush_id) {
@@ -757,8 +815,7 @@
     int raw_fd = pending_process.shmem.fd();
     // TODO(fmayer): Full buffer could deadlock us here.
     if (!self->Send(&data_source.client_configuration,
-                    sizeof(data_source.client_configuration), &raw_fd, 1,
-                    base::UnixSocket::BlockingMode::kBlocking)) {
+                    sizeof(data_source.client_configuration), &raw_fd, 1)) {
       // If Send fails, the socket will have been Shutdown, and the raw socket
       // closed.
       producer_->pending_processes_.erase(it);
@@ -912,6 +969,11 @@
   process_state.unwinding_time_us.Add(alloc_rec.unwinding_time_us);
   process_state.total_unwinding_time_us += alloc_rec.unwinding_time_us;
 
+  // abspc may no longer refer to the same functions, as we had to reparse
+  // maps. Reset the cache.
+  if (alloc_rec.reparsed_map)
+    heap_tracker.ClearFrameCache();
+
   heap_tracker.RecordMalloc(alloc_rec.frames, alloc_metadata.alloc_address,
                             alloc_metadata.sample_size,
                             alloc_metadata.alloc_size,
@@ -955,13 +1017,16 @@
       !ds->shutting_down) {
     return false;
   }
+
+  bool was_stopped = ds->was_stopped;
   DataSourceInstanceID ds_id = ds->id;
   auto weak_producer = weak_factory_.GetWeakPtr();
-  ds->trace_writer->Flush([weak_producer, ds_id] {
+  ds->trace_writer->Flush([weak_producer, ds_id, was_stopped] {
     if (!weak_producer)
       return;
 
-    weak_producer->endpoint_->NotifyDataSourceStopped(ds_id);
+    if (was_stopped)
+      weak_producer->endpoint_->NotifyDataSourceStopped(ds_id);
     weak_producer->data_sources_.erase(ds_id);
 
     if (weak_producer->mode_ == HeapprofdMode::kChild) {
@@ -1001,5 +1066,90 @@
   MaybeFinishDataSource(&ds);
 }
 
+void HeapprofdProducer::CheckDataSourceCpu() {
+  auto weak_producer = weak_factory_.GetWeakPtr();
+  task_runner_->PostDelayedTask(
+      [weak_producer] {
+        if (!weak_producer)
+          return;
+        weak_producer->CheckDataSourceCpu();
+      },
+      kGuardrailIntervalMs);
+
+  bool any_guardrail = false;
+  for (auto& id_and_ds : data_sources_) {
+    DataSource& ds = id_and_ds.second;
+    if (ds.config.max_heapprofd_cpu_secs() > 0)
+      any_guardrail = true;
+  }
+
+  if (!any_guardrail)
+    return;
+
+  base::Optional<uint64_t> cputime_sec = GetCputimeSec();
+  if (!cputime_sec) {
+    PERFETTO_ELOG("Failed to get CPU time.");
+    return;
+  }
+
+  for (auto& id_and_ds : data_sources_) {
+    DataSource& ds = id_and_ds.second;
+    uint64_t ds_max_cpu = ds.config.max_heapprofd_cpu_secs();
+    if (ds_max_cpu > 0) {
+      // We reject data-sources with CPU guardrails if we cannot read the
+      // initial value.
+      PERFETTO_CHECK(ds.start_cputime_sec);
+      uint64_t cpu_diff = *cputime_sec - *ds.start_cputime_sec;
+      if (*cputime_sec > *ds.start_cputime_sec && cpu_diff > ds_max_cpu) {
+        PERFETTO_ELOG(
+            "Exceeded data-source CPU guardrail "
+            "(%" PRIu64 " > %" PRIu64 "). Shutting down.",
+            cpu_diff, ds_max_cpu);
+        ds.hit_guardrail = true;
+        ShutdownDataSource(&ds);
+      }
+    }
+  }
+}
+
+void HeapprofdProducer::CheckDataSourceMemory() {
+  auto weak_producer = weak_factory_.GetWeakPtr();
+  task_runner_->PostDelayedTask(
+      [weak_producer] {
+        if (!weak_producer)
+          return;
+        weak_producer->CheckDataSourceMemory();
+      },
+      kGuardrailIntervalMs);
+
+  bool any_guardrail = false;
+  for (auto& id_and_ds : data_sources_) {
+    DataSource& ds = id_and_ds.second;
+    if (ds.config.max_heapprofd_memory_kb() > 0)
+      any_guardrail = true;
+  }
+
+  if (!any_guardrail)
+    return;
+
+  base::Optional<uint32_t> anon_and_swap = GetRssAnonAndSwap(getpid());
+  if (!anon_and_swap) {
+    PERFETTO_ELOG("Failed to read heapprofd memory.");
+    return;
+  }
+
+  for (auto& id_and_ds : data_sources_) {
+    DataSource& ds = id_and_ds.second;
+    uint32_t ds_max_mem = ds.config.max_heapprofd_memory_kb();
+    if (ds_max_mem > 0 && *anon_and_swap > ds_max_mem) {
+      PERFETTO_ELOG("Exceeded data-source memory guardrail (%" PRIu32
+                    " > %" PRIu32 "). Shutting down.",
+                    *anon_and_swap, ds_max_mem);
+      ds.hit_guardrail = true;
+      ShutdownDataSource(&ds);
+    }
+  }
+}
+
 }  // namespace profiling
 }  // namespace perfetto
diff --git a/src/profiling/memory/heapprofd_producer.h b/src/profiling/memory/heapprofd_producer.h
index e441507..7cf3367 100644
--- a/src/profiling/memory/heapprofd_producer.h
+++ b/src/profiling/memory/heapprofd_producer.h
@@ -33,10 +33,11 @@
 #include "perfetto/ext/tracing/core/tracing_service.h"
 #include "perfetto/tracing/core/data_source_config.h"
 
+#include "src/profiling/common/interning_output.h"
+#include "src/profiling/common/proc_utils.h"
 #include "src/profiling/memory/bookkeeping.h"
 #include "src/profiling/memory/bookkeeping_dump.h"
 #include "src/profiling/memory/page_idle_checker.h"
-#include "src/profiling/memory/proc_utils.h"
 #include "src/profiling/memory/system_property.h"
 #include "src/profiling/memory/unwinding.h"
 
@@ -200,10 +201,13 @@
     std::set<pid_t> rejected_pids;
     std::map<pid_t, ProcessState> process_states;
     std::vector<std::string> normalized_cmdlines;
-    DumpState::InternState intern_state;
+    InterningOutputTracker intern_state;
     bool shutting_down = false;
     bool started = false;
+    bool hit_guardrail = false;
+    bool was_stopped = false;
     uint32_t stop_timeout_ms;
+    base::Optional<uint64_t> start_cputime_sec;
   };
 
   struct PendingProcess {
@@ -220,6 +224,11 @@
   void ResetConnectionBackoff();
   void IncreaseConnectionBackoff();
 
+  base::Optional<uint64_t> GetCputimeSec();
+
+  void CheckDataSourceMemory();
+  void CheckDataSourceCpu();
+
   void FinishDataSourceFlush(FlushRequestID flush_id);
   bool DumpProcessesInDataSource(DataSourceInstanceID id);
   void DumpProcessState(DataSource* ds, pid_t pid, ProcessState* process);
@@ -243,6 +252,7 @@
   // Specific to mode_ == kChild
   void AdoptTargetProcessSocket();
 
+  void ShutdownDataSource(DataSource* ds);
   bool MaybeFinishDataSource(DataSource* ds);
 
   // Class state:
@@ -284,6 +294,7 @@
   base::ScopedFile inherited_fd_;
 
   SocketDelegate socket_delegate_;
+  base::ScopedFile stat_fd_;
 
   base::WeakPtrFactory<HeapprofdProducer> weak_factory_;  // Keep last.
 };
diff --git a/src/profiling/memory/heapprofd_producer_unittest.cc b/src/profiling/memory/heapprofd_producer_unittest.cc
index 66bd452..dd5b0d1 100644
--- a/src/profiling/memory/heapprofd_producer_unittest.cc
+++ b/src/profiling/memory/heapprofd_producer_unittest.cc
@@ -41,13 +41,15 @@
   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_CONST_METHOD0(IsShmemProvidedByProducer, bool());
   MOCK_METHOD1(ActivateTriggers, void(const std::vector<std::string>&));
 
   MOCK_METHOD1(RegisterDataSource, void(const DataSourceDescriptor&));
   MOCK_METHOD2(CommitData, void(const CommitDataRequest&, CommitDataCallback));
   MOCK_METHOD2(RegisterTraceWriter, void(uint32_t, uint32_t));
   MOCK_METHOD1(UnregisterTraceWriter, void(uint32_t));
+  MOCK_METHOD1(Sync, void(std::function<void()>));
 };
 
 TEST(LogHistogramTest, Simple) {
diff --git a/src/profiling/memory/java_hprof_producer.cc b/src/profiling/memory/java_hprof_producer.cc
index 9d101ff..4d015c2 100644
--- a/src/profiling/memory/java_hprof_producer.cc
+++ b/src/profiling/memory/java_hprof_producer.cc
@@ -18,8 +18,9 @@
 
 #include <signal.h>
 
+#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/tracing/core/trace_writer.h"
-#include "src/profiling/memory/proc_utils.h"
+#include "src/profiling/common/proc_utils.h"
 
 namespace perfetto {
 namespace profiling {
@@ -82,8 +83,16 @@
   ds.id = id;
   for (uint64_t pid : config.pid())
     ds.pids.emplace(static_cast<pid_t>(pid));
-  auto normalized_cmdlines = NormalizeCmdlines(config.process_cmdline());
-  FindPidsForCmdlines(normalized_cmdlines, &ds.pids);
+  base::Optional<std::vector<std::string>> normalized_cmdlines =
+      NormalizeCmdlines(config.process_cmdline());
+  if (!normalized_cmdlines.has_value()) {
+    PERFETTO_ELOG("Rejecting data source due to invalid cmdline in config.");
+    return;
+  }
+  FindPidsForCmdlines(normalized_cmdlines.value(), &ds.pids);
+  if (config.min_anonymous_memory_kb() > 0)
+    RemoveUnderAnonThreshold(config.min_anonymous_memory_kb(), &ds.pids);
+
   ds.config = std::move(config);
   data_sources_.emplace(id, std::move(ds));
 }
diff --git a/src/profiling/memory/malloc_hooks.cc b/src/profiling/memory/malloc_hooks.cc
index c53f79f..fcb3923 100644
--- a/src/profiling/memory/malloc_hooks.cc
+++ b/src/profiling/memory/malloc_hooks.cc
@@ -35,8 +35,8 @@
 #include "perfetto/ext/base/no_destructor.h"
 #include "perfetto/ext/base/unix_socket.h"
 #include "perfetto/ext/base/utils.h"
+#include "src/profiling/common/proc_utils.h"
 #include "src/profiling/memory/client.h"
-#include "src/profiling/memory/proc_utils.h"
 #include "src/profiling/memory/scoped_spinlock.h"
 #include "src/profiling/memory/unhooked_allocator.h"
 #include "src/profiling/memory/wire_protocol.h"
@@ -220,36 +220,21 @@
   return prop_value;
 }
 
-bool ShouldForkPrivateDaemon() {
-  std::string build_type = ReadSystemProperty("ro.build.type");
-  if (build_type.empty()) {
-    PERFETTO_ELOG(
-        "Cannot determine platform build type, proceeding in fork mode "
-        "profiling.");
-    return true;
-  }
-
-  // On development builds, we support both modes of profiling, depending on a
-  // system property.
-  if (build_type == "userdebug" || build_type == "eng") {
-    // Note: if renaming the property, also update system_property.cc
-    std::string mode = ReadSystemProperty("heapprofd.userdebug.mode");
-    return mode == "fork";
-  }
-
-  // User/other builds - always fork private profiler.
-  return true;
+bool ForceForkPrivateDaemon() {
+  // Note: if renaming the property, also update system_property.cc
+  std::string mode = ReadSystemProperty("heapprofd.userdebug.mode");
+  return mode == "fork";
 }
 
 std::shared_ptr<perfetto::profiling::Client> CreateClientForCentralDaemon(
     UnhookedAllocator<perfetto::profiling::Client> unhooked_allocator) {
-  PERFETTO_DLOG("Constructing client for central daemon.");
+  PERFETTO_LOG("Constructing client for central daemon.");
   using perfetto::profiling::Client;
 
   perfetto::base::Optional<perfetto::base::UnixSocketRaw> sock =
       Client::ConnectToHeapprofd(perfetto::profiling::kHeapprofdSocketFile);
   if (!sock) {
-    PERFETTO_ELOG("Failed to connect to %s.",
+    PERFETTO_ELOG("Failed to connect to %s. This is benign on user builds.",
                   perfetto::profiling::kHeapprofdSocketFile);
     return nullptr;
   }
@@ -259,7 +244,7 @@
 
 std::shared_ptr<perfetto::profiling::Client> CreateClientAndPrivateDaemon(
     UnhookedAllocator<perfetto::profiling::Client> unhooked_allocator) {
-  PERFETTO_DLOG("Setting up fork mode profiling.");
+  PERFETTO_LOG("Setting up fork mode profiling.");
   perfetto::base::UnixSocketRaw parent_sock;
   perfetto::base::UnixSocketRaw child_sock;
   std::tie(parent_sock, child_sock) = perfetto::base::UnixSocketRaw::CreatePair(
@@ -388,7 +373,8 @@
       AbortOnSpinlockTimeout();
 
     if (g_client.ref()) {
-      PERFETTO_LOG("Rejecting concurrent profiling initialization.");
+      PERFETTO_LOG("%s: Rejecting concurrent profiling initialization.",
+                   getprogname());
       return true;  // success as we're in a valid state
     }
     old_client = g_client.ref();
@@ -404,16 +390,18 @@
 
   // These factory functions use heap objects, so we need to run them without
   // the spinlock held.
-  std::shared_ptr<Client> client =
-      ShouldForkPrivateDaemon()
-          ? CreateClientAndPrivateDaemon(unhooked_allocator)
-          : CreateClientForCentralDaemon(unhooked_allocator);
+  std::shared_ptr<Client> client;
+  if (!ForceForkPrivateDaemon())
+    client = CreateClientForCentralDaemon(unhooked_allocator);
+  if (!client)
+    client = CreateClientAndPrivateDaemon(unhooked_allocator);
 
   if (!client) {
-    PERFETTO_LOG("heapprofd_client not initialized, not installing hooks.");
+    PERFETTO_LOG("%s: heapprofd_client not initialized, not installing hooks.",
+                 getprogname());
     return false;
   }
-  PERFETTO_LOG("heapprofd_client initialized.");
+  PERFETTO_LOG("%s: heapprofd_client initialized.", getprogname());
   {
     ScopedSpinlock s(&g_client_lock, ScopedSpinlock::Mode::Try);
     if (PERFETTO_UNLIKELY(!s.locked()))
@@ -620,13 +608,14 @@
   return dispatch->malloc_info(options, fp);
 }
 
-int HEAPPROFD_ADD_PREFIX(_malloc_iterate)(uintptr_t,
-                                          size_t,
-                                          void (*)(uintptr_t base,
+int HEAPPROFD_ADD_PREFIX(_malloc_iterate)(uintptr_t base,
+                                          size_t size,
+                                          void (*callback)(uintptr_t base,
                                                    size_t size,
                                                    void* arg),
-                                          void*) {
-  return 0;
+                                          void* arg) {
+  const MallocDispatch* dispatch = GetDispatch();
+  return dispatch->malloc_iterate(base, size, callback, arg);
 }
 
 void HEAPPROFD_ADD_PREFIX(_malloc_disable)() {
diff --git a/src/profiling/memory/page_idle_checker.cc b/src/profiling/memory/page_idle_checker.cc
index 7e639b5..a5ac79e 100644
--- a/src/profiling/memory/page_idle_checker.cc
+++ b/src/profiling/memory/page_idle_checker.cc
@@ -16,7 +16,6 @@
 
 #include "src/profiling/memory/page_idle_checker.h"
 #include "perfetto/ext/base/utils.h"
-#include "src/profiling/memory/utils.h"
 
 #include <inttypes.h>
 #include <vector>
@@ -74,8 +73,7 @@
   off64_t offset = 8 * (virt_page_nr / 64);
   size_t bit_offset = virt_page_nr % 64;
   uint64_t bit_pattern = 1 << bit_offset;
-  if (WriteAtOffsetClobberSeekPos(*page_idle_fd_, &bit_pattern,
-                                  sizeof(bit_pattern), offset) !=
+  if (pwrite64(*page_idle_fd_, &bit_pattern, sizeof(bit_pattern), offset) !=
       static_cast<ssize_t>(sizeof(bit_pattern))) {
     PERFETTO_PLOG("Failed to write bit pattern at %" PRIi64 ".", offset);
   }
@@ -85,8 +83,7 @@
   off64_t offset = 8 * (virt_page_nr / 64);
   size_t bit_offset = virt_page_nr % 64;
   uint64_t bit_pattern;
-  if (ReadAtOffsetClobberSeekPos(*page_idle_fd_, &bit_pattern,
-                                 sizeof(bit_pattern), offset) !=
+  if (pread64(*page_idle_fd_, &bit_pattern, sizeof(bit_pattern), offset) !=
       static_cast<ssize_t>(sizeof(bit_pattern))) {
     PERFETTO_PLOG("Failed to read bit pattern at %" PRIi64 ".", offset);
     return -1;
diff --git a/src/profiling/memory/parse_smaps_unittest.cc b/src/profiling/memory/parse_smaps_unittest.cc
new file mode 100644
index 0000000..9ee4c82
--- /dev/null
+++ b/src/profiling/memory/parse_smaps_unittest.cc
@@ -0,0 +1,93 @@
+/*
+ * 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 "perfetto/profiling/parse_smaps.h"
+
+#include "perfetto/ext/base/scoped_file.h"
+#include "src/base/test/utils.h"
+#include "test/gtest_and_gmock.h"
+
+#include <inttypes.h>
+
+namespace perfetto {
+namespace profiling {
+
+bool operator==(const SmapsEntry& a, const SmapsEntry& b);
+bool operator==(const SmapsEntry& a, const SmapsEntry& b) {
+  return a.pathname == b.pathname && a.size_kb == b.size_kb &&
+         a.private_dirty_kb == b.private_dirty_kb && a.swap_kb == b.swap_kb;
+}
+
+namespace {
+
+using ::testing::ElementsAre;
+
+TEST(ParseSmapsTest, Smoke) {
+  base::ScopedFstream fd(fopen(
+      base::GetTestDataPath("src/profiling/memory/test/data/cat_smaps").c_str(),
+      "r"));
+  std::vector<SmapsEntry> entries;
+  EXPECT_TRUE(ParseSmaps(
+      *fd, [&entries](const SmapsEntry& e) { entries.emplace_back(e); }));
+
+  SmapsEntry cat1;
+  cat1.pathname = "/bin/cat";
+  cat1.size_kb = 8;
+  cat1.private_dirty_kb = 0;
+  cat1.swap_kb = 0;
+  SmapsEntry cat2;
+  cat2.pathname = "/bin/cat";
+  cat2.size_kb = 8;
+  cat2.private_dirty_kb = 0;
+  cat2.swap_kb = 0;
+  SmapsEntry heap;
+  heap.pathname = "[heap stuff]";
+  heap.size_kb = 132;
+  heap.private_dirty_kb = 8;
+  heap.swap_kb = 4;
+  EXPECT_THAT(entries, ElementsAre(cat1, cat2, heap));
+}
+
+TEST(ParseSmapsTest, SmokeNoEol) {
+  base::ScopedFstream fd(fopen(
+      base::GetTestDataPath("src/profiling/memory/test/data/cat_smaps_noeol")
+          .c_str(),
+      "r"));
+  std::vector<SmapsEntry> entries;
+  EXPECT_TRUE(ParseSmaps(
+      *fd, [&entries](const SmapsEntry& e) { entries.emplace_back(e); }));
+
+  SmapsEntry cat1;
+  cat1.pathname = "/bin/cat";
+  cat1.size_kb = 8;
+  cat1.private_dirty_kb = 0;
+  cat1.swap_kb = 0;
+  SmapsEntry cat2;
+  cat2.pathname = "/bin/cat";
+  cat2.size_kb = 8;
+  cat2.private_dirty_kb = 0;
+  cat2.swap_kb = 0;
+  SmapsEntry heap;
+  heap.pathname = "[heap stuff]";
+  heap.size_kb = 132;
+  heap.private_dirty_kb = 8;
+  heap.swap_kb = 4;
+  EXPECT_THAT(entries, ElementsAre(cat1, cat2, heap));
+}
+
+}  // namespace
+}  // namespace profiling
+}  // namespace perfetto
diff --git a/src/profiling/memory/shared_ring_buffer.cc b/src/profiling/memory/shared_ring_buffer.cc
index 99a9c89..0eee59d 100644
--- a/src/profiling/memory/shared_ring_buffer.cc
+++ b/src/profiling/memory/shared_ring_buffer.cc
@@ -62,8 +62,8 @@
 
   if (!fd) {
 #if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
-    // In-tree builds should only allow mem_fd, so we can inspect the seals
-    // to verify the fd is appropriately sealed.
+    // In-tree builds only allow mem_fd, so we can inspect the seals to verify
+    // the fd is appropriately sealed.
     PERFETTO_ELOG("memfd_create() failed");
     return;
 #else
@@ -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/test/data/cat_smaps b/src/profiling/memory/test/data/cat_smaps
new file mode 100644
index 0000000..70b49b1
--- /dev/null
+++ b/src/profiling/memory/test/data/cat_smaps
@@ -0,0 +1,66 @@
+5614e178c000-5614e178e000 r--p 00000000 fe:01 393326                     /bin/cat
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         8 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+LazyFree:              0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+THPeligible:    0
+VmFlags: rd mr mw me dw sd 
+5614e1793000-5614e1795000 r--p 00007000 fe:01 393326                     /bin/cat
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         8 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+LazyFree:              0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+THPeligible:    0
+VmFlags: rd mr mw me dw sd 
+5614e184c000-5614e186d000 rw-p 00000000 00:00 0                          [heap stuff]
+Size:                132 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+LazyFree:              0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  4 kB
+SwapPss:               4 kB
+Locked:                0 kB
+THPeligible:    1
+VmFlags: rd wr mr mw me ac sd 
diff --git a/src/profiling/memory/test/data/cat_smaps_noeol b/src/profiling/memory/test/data/cat_smaps_noeol
new file mode 100644
index 0000000..40cbfcb
--- /dev/null
+++ b/src/profiling/memory/test/data/cat_smaps_noeol
@@ -0,0 +1,66 @@
+5614e178c000-5614e178e000 r--p 00000000 fe:01 393326                     /bin/cat
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         8 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+LazyFree:              0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+THPeligible:    0
+VmFlags: rd mr mw me dw sd 
+5614e1793000-5614e1795000 r--p 00007000 fe:01 393326                     /bin/cat
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         8 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+LazyFree:              0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+THPeligible:    0
+VmFlags: rd mr mw me dw sd 
+5614e184c000-5614e186d000 rw-p 00000000 00:00 0                          [heap stuff]
+Size:                132 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+LazyFree:              0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  4 kB
+SwapPss:               4 kB
+Locked:                0 kB
+THPeligible:    1
+VmFlags: rd wr mr mw me ac sd 
\ No newline at end of file
diff --git a/src/profiling/memory/unwinding.cc b/src/profiling/memory/unwinding.cc
index ad18031..c9e43fe 100644
--- a/src/profiling/memory/unwinding.cc
+++ b/src/profiling/memory/unwinding.cc
@@ -51,7 +51,6 @@
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/thread_task_runner.h"
 
-#include "src/profiling/memory/utils.h"
 #include "src/profiling/memory/wire_protocol.h"
 
 namespace perfetto {
@@ -117,65 +116,6 @@
   return ret;
 }
 
-StackOverlayMemory::StackOverlayMemory(std::shared_ptr<unwindstack::Memory> mem,
-                                       uint64_t sp,
-                                       uint8_t* stack,
-                                       size_t size)
-    : mem_(std::move(mem)), sp_(sp), stack_end_(sp + size), stack_(stack) {}
-
-size_t StackOverlayMemory::Read(uint64_t addr, void* dst, size_t size) {
-  if (addr >= sp_ && addr + size <= stack_end_ && addr + size > sp_) {
-    size_t offset = static_cast<size_t>(addr - sp_);
-    memcpy(dst, stack_ + offset, size);
-    return size;
-  }
-
-  return mem_->Read(addr, dst, size);
-}
-
-FDMemory::FDMemory(base::ScopedFile mem_fd) : mem_fd_(std::move(mem_fd)) {}
-
-size_t FDMemory::Read(uint64_t addr, void* dst, size_t size) {
-  ssize_t rd = ReadAtOffsetClobberSeekPos(*mem_fd_, dst, size,
-                                          static_cast<off64_t>(addr));
-  if (rd == -1) {
-    PERFETTO_DPLOG("read of %zu at offset %" PRIu64, size, addr);
-    return 0;
-  }
-  return static_cast<size_t>(rd);
-}
-
-FileDescriptorMaps::FileDescriptorMaps(base::ScopedFile fd)
-    : fd_(std::move(fd)) {}
-
-bool FileDescriptorMaps::Parse() {
-  // If the process has already exited, lseek or ReadFileDescriptor will
-  // return false.
-  if (lseek(*fd_, 0, SEEK_SET) == -1)
-    return false;
-
-  std::string content;
-  if (!base::ReadFileDescriptor(*fd_, &content))
-    return false;
-  return android::procinfo::ReadMapFileContent(
-      &content[0], [&](uint64_t start, uint64_t end, uint16_t flags,
-                       uint64_t pgoff, ino_t, const char* name) {
-        // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
-        if (strncmp(name, "/dev/", 5) == 0 &&
-            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));
-      });
-}
-
-void FileDescriptorMaps::Reset() {
-  maps_.clear();
-}
-
 bool DoUnwind(WireMessage* msg, UnwindingMetadata* metadata, AllocRecord* out) {
   AllocMetadata* alloc_metadata = msg->alloc_header;
   std::unique_ptr<unwindstack::Regs> regs(CreateRegsFromRawData(
@@ -196,14 +136,15 @@
                                            alloc_metadata->stack_pointer, stack,
                                            msg->payload_size);
 
-  unwindstack::Unwinder unwinder(kMaxFrames, &metadata->maps, regs.get(), mems);
+  unwindstack::Unwinder unwinder(kMaxFrames, &metadata->fd_maps, regs.get(),
+                                 mems);
 #if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
   unwinder.SetJitDebug(metadata->jit_debug.get(), regs->Arch());
   unwinder.SetDexFiles(metadata->dex_files.get(), regs->Arch());
 #endif
-  // Surpress incorrect "variable may be uninitialized" error for if condition
+  // Suppress incorrect "variable may be uninitialized" error for if condition
   // after this loop. error_code = LastErrorCode gets run at least once.
-  uint8_t error_code = 0;
+  unwindstack::ErrorCode error_code = unwindstack::ERROR_NONE;
   for (int attempt = 0; attempt < 2; ++attempt) {
     if (attempt > 0) {
       if (metadata->last_maps_reparse_time + kMapsReparseInterval >
@@ -223,30 +164,24 @@
       unwinder.SetDexFiles(metadata->dex_files.get(), regs->Arch());
 #endif
     }
-    unwinder.Unwind(&kSkipMaps, nullptr);
+    unwinder.Unwind(&kSkipMaps, /*map_suffixes_to_ignore=*/nullptr);
     error_code = unwinder.LastErrorCode();
     if (error_code != unwindstack::ERROR_INVALID_MAP)
       break;
   }
   std::vector<unwindstack::FrameData> frames = unwinder.ConsumeFrames();
   for (unwindstack::FrameData& fd : frames) {
-    std::string build_id;
-    if (fd.map_name != "") {
-      unwindstack::MapInfo* map_info = metadata->maps.Find(fd.pc);
-      if (map_info)
-        build_id = map_info->GetBuildID();
-    }
-
-    out->frames.emplace_back(std::move(fd), std::move(build_id));
+    out->frames.emplace_back(metadata->AnnotateFrame(std::move(fd)));
   }
 
-  if (error_code != 0) {
+  if (error_code != unwindstack::ERROR_NONE) {
     PERFETTO_DLOG("Unwinding error %" PRIu8, error_code);
     unwindstack::FrameData frame_data{};
-    frame_data.function_name = "ERROR " + std::to_string(error_code);
+    frame_data.function_name =
+        "ERROR " + StringifyLibUnwindstackError(error_code);
     frame_data.map_name = "ERROR";
 
-    out->frames.emplace_back(frame_data, "");
+    out->frames.emplace_back(std::move(frame_data), "");
     out->error = true;
   }
   return true;
@@ -383,7 +318,7 @@
       base::SockFamily::kUnix, base::SockType::kStream);
   pid_t peer_pid = sock->peer_pid();
 
-  UnwindingMetadata metadata(peer_pid, std::move(handoff_data.maps_fd),
+  UnwindingMetadata metadata(std::move(handoff_data.maps_fd),
                              std::move(handoff_data.mem_fd));
   ClientData client_data{
       handoff_data.data_source_instance_id,
diff --git a/src/profiling/memory/unwinding.h b/src/profiling/memory/unwinding.h
index a702c64..82ec111 100644
--- a/src/profiling/memory/unwinding.h
+++ b/src/profiling/memory/unwinding.h
@@ -17,20 +17,13 @@
 #ifndef SRC_PROFILING_MEMORY_UNWINDING_H_
 #define SRC_PROFILING_MEMORY_UNWINDING_H_
 
-#include "perfetto/base/build_config.h"
-
-#include <unwindstack/Maps.h>
-#include <unwindstack/Unwinder.h>
-
-#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
-#include <unwindstack/DexFiles.h>
-#include <unwindstack/JitDebug.h>
-#endif
+#include <unwindstack/Regs.h>
 
 #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"
+#include "src/profiling/common/unwind_support.h"
 #include "src/profiling/memory/bookkeeping.h"
 #include "src/profiling/memory/unwound_messages.h"
 #include "src/profiling/memory/wire_protocol.h"
@@ -42,101 +35,6 @@
     unwindstack::ArchEnum arch,
     void* raw_data);
 
-// Read /proc/[pid]/maps from an open file descriptor.
-// TODO(fmayer): Figure out deduplication to other maps.
-class FileDescriptorMaps : public unwindstack::Maps {
- public:
-  FileDescriptorMaps(base::ScopedFile fd);
-
-  FileDescriptorMaps(const FileDescriptorMaps&) = delete;
-  FileDescriptorMaps& operator=(const FileDescriptorMaps&) = delete;
-
-  FileDescriptorMaps(FileDescriptorMaps&& m) : Maps(std::move(m)) {
-    fd_ = std::move(m.fd_);
-  }
-
-  FileDescriptorMaps& operator=(FileDescriptorMaps&& m) {
-    if (&m != this)
-      fd_ = std::move(m.fd_);
-    Maps::operator=(std::move(m));
-    return *this;
-  }
-
-  virtual ~FileDescriptorMaps() override = default;
-
-  bool Parse() override;
-  void Reset();
-
- private:
-  base::ScopedFile fd_;
-};
-
-class FDMemory : public unwindstack::Memory {
- public:
-  FDMemory(base::ScopedFile mem_fd);
-  size_t Read(uint64_t addr, void* dst, size_t size) override;
-
- private:
-  base::ScopedFile mem_fd_;
-};
-
-// Overlays size bytes pointed to by stack for addresses in [sp, sp + size).
-// Addresses outside of that range are read from mem_fd, which should be an fd
-// that opened /proc/[pid]/mem.
-class StackOverlayMemory : public unwindstack::Memory {
- public:
-  StackOverlayMemory(std::shared_ptr<unwindstack::Memory> mem,
-                     uint64_t sp,
-                     uint8_t* stack,
-                     size_t size);
-  size_t Read(uint64_t addr, void* dst, size_t size) override;
-
- private:
-  std::shared_ptr<unwindstack::Memory> mem_;
-  uint64_t sp_;
-  uint64_t stack_end_;
-  uint8_t* stack_;
-};
-
-struct UnwindingMetadata {
-  UnwindingMetadata(pid_t p, base::ScopedFile maps_fd, base::ScopedFile mem)
-      : pid(p),
-        maps(std::move(maps_fd)),
-        fd_mem(std::make_shared<FDMemory>(std::move(mem)))
-#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
-        ,
-        jit_debug(std::unique_ptr<unwindstack::JitDebug>(
-            new unwindstack::JitDebug(fd_mem))),
-        dex_files(std::unique_ptr<unwindstack::DexFiles>(
-            new unwindstack::DexFiles(fd_mem)))
-#endif
-  {
-    bool parsed = maps.Parse();
-    PERFETTO_DCHECK(parsed);
-  }
-  void ReparseMaps() {
-    reparses++;
-    maps.Reset();
-    maps.Parse();
-#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
-    jit_debug = std::unique_ptr<unwindstack::JitDebug>(
-        new unwindstack::JitDebug(fd_mem));
-    dex_files = std::unique_ptr<unwindstack::DexFiles>(
-        new unwindstack::DexFiles(fd_mem));
-#endif
-  }
-  pid_t pid;
-  FileDescriptorMaps maps;
-  // 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;
-#endif
-};
-
 bool DoUnwind(WireMessage*, UnwindingMetadata* metadata, AllocRecord* out);
 
 class UnwindingWorker : public base::UnixSocket::EventListener {
@@ -161,8 +59,8 @@
   };
 
   UnwindingWorker(Delegate* delegate, base::ThreadTaskRunner thread_task_runner)
-      : thread_task_runner_(std::move(thread_task_runner)),
-        delegate_(delegate) {}
+      : delegate_(delegate),
+        thread_task_runner_(std::move(thread_task_runner)) {}
 
   // Public API safe to call from other threads.
   void PostDisconnectSocket(pid_t pid);
@@ -199,13 +97,23 @@
     ClientConfiguration client_config;
   };
 
-  // Task runner with a dedicated thread. Keep at the start of the data member
-  // declarations, such that it is valid during construction & destruction of
-  // the other members.
-  base::ThreadTaskRunner thread_task_runner_;
-
   std::map<pid_t, ClientData> client_data_;
   Delegate* delegate_;
+
+  // Task runner with a dedicated thread. Keep last as instances this class are
+  // currently (incorrectly) being destroyed on the main thread, instead of the
+  // task thread. By destroying this task runner first, we ensure that the
+  // UnwindingWorker is not active while the rest of its state is being
+  // destroyed. Additionally this ensures that the destructing thread sees a
+  // consistent view of the memory due to the ThreadTaskRunner's destructor
+  // joining a thread.
+  //
+  // Additionally, keep the destructor defaulted, as its body would still race
+  // against an active task thread.
+  //
+  // TODO(rsavitski): make the task thread own the object's lifetime (likely by
+  // refactoring base::ThreadTaskRunner).
+  base::ThreadTaskRunner thread_task_runner_;
 };
 
 }  // namespace profiling
diff --git a/src/profiling/memory/unwinding_fuzzer.cc b/src/profiling/memory/unwinding_fuzzer.cc
index c2046b0..e87202e 100644
--- a/src/profiling/memory/unwinding_fuzzer.cc
+++ b/src/profiling/memory/unwinding_fuzzer.cc
@@ -19,6 +19,7 @@
 
 #include "perfetto/ext/base/utils.h"
 #include "perfetto/ext/tracing/core/basic_types.h"
+#include "src/profiling/common/unwind_support.h"
 #include "src/profiling/memory/shared_ring_buffer.h"
 #include "src/profiling/memory/unwinding.h"
 #include "src/profiling/memory/unwound_messages.h"
@@ -40,8 +41,7 @@
 
   pid_t self_pid = getpid();
   DataSourceInstanceID id = 0;
-  UnwindingMetadata metadata(self_pid,
-                             base::OpenFile("/proc/self/maps", O_RDONLY),
+  UnwindingMetadata metadata(base::OpenFile("/proc/self/maps", O_RDONLY),
                              base::OpenFile("/proc/self/mem", O_RDONLY));
 
   NopDelegate nop_delegate;
diff --git a/src/profiling/memory/unwinding_unittest.cc b/src/profiling/memory/unwinding_unittest.cc
index b52a7ce..99fe08b 100644
--- a/src/profiling/memory/unwinding_unittest.cc
+++ b/src/profiling/memory/unwinding_unittest.cc
@@ -23,6 +23,7 @@
 #include <unwindstack/RegsGetLocal.h>
 
 #include "perfetto/ext/base/scoped_file.h"
+#include "src/profiling/common/unwind_support.h"
 #include "src/profiling/memory/client.h"
 #include "src/profiling/memory/wire_protocol.h"
 #include "test/gtest_and_gmock.h"
@@ -57,10 +58,10 @@
   ASSERT_EQ(buf[0], value);
 }
 
-TEST(UnwindingTest, FileDescriptorMapsParse) {
+TEST(UnwindingTest, FDMapsParse) {
   base::ScopedFile proc_maps(base::OpenFile("/proc/self/maps", O_RDONLY));
   ASSERT_TRUE(proc_maps);
-  FileDescriptorMaps maps(std::move(proc_maps));
+  FDMaps maps(std::move(proc_maps));
   ASSERT_TRUE(maps.Parse());
   unwindstack::MapInfo* map_info =
       maps.Find(reinterpret_cast<uint64_t>(&proc_maps));
@@ -138,8 +139,7 @@
   base::ScopedFile proc_maps(base::OpenFile("/proc/self/maps", O_RDONLY));
   base::ScopedFile proc_mem(base::OpenFile("/proc/self/mem", O_RDONLY));
   GlobalCallstackTrie callsites;
-  UnwindingMetadata metadata(getpid(), std::move(proc_maps),
-                             std::move(proc_mem));
+  UnwindingMetadata metadata(std::move(proc_maps), std::move(proc_mem));
   WireMessage msg;
   auto record = GetRecord(&msg);
   AllocRecord out;
@@ -159,10 +159,9 @@
   base::ScopedFile proc_maps(base::OpenFile("/proc/self/maps", O_RDONLY));
   base::ScopedFile proc_mem(base::OpenFile("/proc/self/mem", O_RDONLY));
   GlobalCallstackTrie callsites;
-  UnwindingMetadata metadata(getpid(), std::move(proc_maps),
-                             std::move(proc_mem));
+  UnwindingMetadata metadata(std::move(proc_maps), std::move(proc_mem));
   // Force reparse in DoUnwind.
-  metadata.maps.Reset();
+  metadata.fd_maps.Reset();
   WireMessage msg;
   auto record = GetRecord(&msg);
   AllocRecord out;
diff --git a/src/profiling/memory/unwound_messages.h b/src/profiling/memory/unwound_messages.h
index 15608dd..b7101af 100644
--- a/src/profiling/memory/unwound_messages.h
+++ b/src/profiling/memory/unwound_messages.h
@@ -20,20 +20,12 @@
 #include <unwindstack/Maps.h>
 #include <unwindstack/Unwinder.h>
 
+#include "src/profiling/common/unwind_support.h"
 #include "src/profiling/memory/wire_protocol.h"
 
 namespace perfetto {
 namespace profiling {
 
-// A wrapper of libunwindstack FrameData that also includes the build_id.
-struct FrameData {
-  FrameData(unwindstack::FrameData f, std::string b)
-      : frame(std::move(f)), build_id(std::move(b)) {}
-
-  unwindstack::FrameData frame;
-  std::string build_id;
-};
-
 // Single allocation with an unwound callstack.
 struct AllocRecord {
   pid_t pid;
diff --git a/src/profiling/memory/utils.cc b/src/profiling/memory/utils.cc
deleted file mode 100644
index b424192..0000000
--- a/src/profiling/memory/utils.cc
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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/profiling/memory/utils.h"
-
-namespace perfetto {
-namespace profiling {
-
-// Behaves as a pread64, emulating it if not already exposed by the standard
-// library. Safe to use on 32bit platforms for addresses with the top bit set.
-// Clobbers the |fd| seek position if emulating.
-ssize_t ReadAtOffsetClobberSeekPos(int fd,
-                                   void* buf,
-                                   size_t count,
-                                   off64_t addr) {
-#ifdef __BIONIC__
-  return pread64(fd, buf, count, addr);
-#else
-  if (lseek64(fd, addr, SEEK_SET) == -1)
-    return -1;
-  return read(fd, buf, count);
-#endif
-}
-
-// Behaves as a pread64, emulating it if not already exposed by the standard
-// library.
-// Clobbers the |fd| seek position if emulating.
-ssize_t WriteAtOffsetClobberSeekPos(int fd,
-                                    void* buf,
-                                    size_t count,
-                                    off64_t addr) {
-#ifdef __BIONIC__
-  return pwrite64(fd, buf, count, addr);
-#else
-  if (lseek64(fd, addr, SEEK_SET) == -1)
-    return -1;
-  return write(fd, buf, count);
-#endif
-}
-
-}  // namespace profiling
-}  // namespace perfetto
diff --git a/src/profiling/memory/utils.h b/src/profiling/memory/utils.h
deleted file mode 100644
index 5e375c9..0000000
--- a/src/profiling/memory/utils.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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_PROFILING_MEMORY_UTILS_H_
-#define SRC_PROFILING_MEMORY_UTILS_H_
-
-#include <unistd.h>
-
-namespace perfetto {
-namespace profiling {
-
-ssize_t ReadAtOffsetClobberSeekPos(int fd,
-                                   void* buf,
-                                   size_t count,
-                                   off64_t addr);
-
-ssize_t WriteAtOffsetClobberSeekPos(int fd,
-                                    void* buf,
-                                    size_t count,
-                                    off64_t addr);
-}
-}  // namespace perfetto
-
-#endif  // SRC_PROFILING_MEMORY_UTILS_H_
diff --git a/src/profiling/memory/wire_protocol.h b/src/profiling/memory/wire_protocol.h
index 1a40b8f..da79c74 100644
--- a/src/profiling/memory/wire_protocol.h
+++ b/src/profiling/memory/wire_protocol.h
@@ -46,6 +46,7 @@
   uint64_t interval;
   bool block_client;
   uint64_t block_client_timeout_us;
+  bool disable_fork_teardown;
 };
 
 // Types needed for the wire format used for communication between the client
diff --git a/src/profiling/perf/BUILD.gn b/src/profiling/perf/BUILD.gn
index b864674..b715de1 100644
--- a/src/profiling/perf/BUILD.gn
+++ b/src/profiling/perf/BUILD.gn
@@ -18,25 +18,21 @@
 
 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") {
   deps = [
+    ":proc_descriptors",
     ":producer",
     "../../../gn:default_deps",
     "../../../src/base",
-    "../../../src/tracing:ipc",
+    "../../../src/tracing/ipc/producer",
   ]
   sources = [
     "traced_perf.cc",
@@ -45,20 +41,31 @@
 }
 
 source_set("producer") {
+  public_deps = [
+    ":common_types",
+    ":regs_parsing",
+    ":unwinding",
+    "../../../include/perfetto/tracing/core",
+    "../../../src/tracing/core:service",  # for metatrace
+  ]
   deps = [
-    ":unwind_support",
+    ":proc_descriptors",
     "../../../gn:default_deps",
+    "../../../include/perfetto/profiling:normalize",
     "../../../protos/perfetto/config:cpp",
     "../../../protos/perfetto/config/profiling:zero",
     "../../../protos/perfetto/trace:zero",
+    "../../../protos/perfetto/trace/profiling:zero",
     "../../../src/base",
     "../../../src/base:unix_socket",
-    "../../../src/tracing:ipc",
-  ]
-  public_deps = [
-    "../../../include/perfetto/tracing/core",
+    "../../../src/tracing/ipc/producer",
+    "../common:callstack_trie",
+    "../common:interner",
+    "../common:interning_output",
+    "../common:proc_utils",
   ]
   sources = [
+    "event_config.cc",
     "event_config.h",
     "event_reader.cc",
     "event_reader.h",
@@ -67,31 +74,73 @@
   ]
 }
 
-source_set("unwind_support") {
+source_set("common_types") {
+  public_deps = [ "../../../gn:libunwindstack" ]
   deps = [
     "../../../gn:default_deps",
+    "../common:unwind_support",
+  ]
+  sources = [ "common_types.h" ]
+}
+
+source_set("unwinding") {
+  public_deps = [
     "../../../gn:libunwindstack",
+    "../../../src/tracing/core:service",  # for metatrace
+  ]
+  deps = [
+    ":common_types",
+    "../../../gn:default_deps",
+    "../../../include/perfetto/ext/tracing/core",
+    "../../../src/base",
+    "../common:unwind_support",
+  ]
+  sources = [
+    "unwind_queue.h",
+    "unwinding.cc",
+    "unwinding.h",
+  ]
+}
+
+source_set("regs_parsing") {
+  public_deps = [ "../../../gn:libunwindstack" ]
+  deps = [
+    "../../../gn:bionic_kernel_uapi_headers",
+    "../../../gn:default_deps",
     "../../../src/base",
   ]
   sources = [
-    "unwind_support.cc",
-    "unwind_support.h",
+    "regs_parsing.cc",
+    "regs_parsing.h",
   ]
 }
+
+source_set("proc_descriptors") {
+  deps = [
+    "../../../gn:default_deps",
+    "../../../src/base",
+  ]
+  sources = [
+    "proc_descriptors.cc",
+    "proc_descriptors.h",
+  ]
+}
+
 source_set("producer_unittests") {
   testonly = true
   deps = [
     ":producer",
+    ":unwinding",
     "../../../gn:default_deps",
     "../../../gn:gtest_and_gmock",
     "../../../protos/perfetto/config:cpp",
-    "../../../protos/perfetto/config:zero",
-    "../../../protos/perfetto/config/profiling:zero",
+    "../../../protos/perfetto/config/profiling:cpp",
     "../../../protos/perfetto/trace:zero",
     "../../../src/protozero",
     "../../base",
   ]
   sources = [
     "event_config_unittest.cc",
+    "unwind_queue_unittest.cc",
   ]
 }
diff --git a/src/profiling/perf/common_types.h b/src/profiling/perf/common_types.h
new file mode 100644
index 0000000..80a6105
--- /dev/null
+++ b/src/profiling/perf/common_types.h
@@ -0,0 +1,91 @@
+/*
+ * 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_PROFILING_PERF_COMMON_TYPES_H_
+#define SRC_PROFILING_PERF_COMMON_TYPES_H_
+
+#include <memory>
+#include <vector>
+
+#include <linux/perf_event.h>
+#include <stdint.h>
+
+#include <unwindstack/Error.h>
+#include <unwindstack/Regs.h>
+
+#include "src/profiling/common/unwind_support.h"
+
+namespace perfetto {
+namespace profiling {
+
+// A parsed perf sample record (PERF_RECORD_SAMPLE from the kernel buffer).
+// Self-contained, used as as input to the callstack unwinding.
+struct ParsedSample {
+  // move-only
+  ParsedSample() = default;
+  ParsedSample(const ParsedSample&) = delete;
+  ParsedSample& operator=(const ParsedSample&) = delete;
+  ParsedSample(ParsedSample&&) noexcept = default;
+  ParsedSample& operator=(ParsedSample&&) noexcept = default;
+
+  uint16_t cpu_mode = PERF_RECORD_MISC_CPUMODE_UNKNOWN;
+  uint32_t cpu = 0;
+  pid_t pid = 0;
+  pid_t tid = 0;
+  uint64_t timestamp = 0;
+  std::unique_ptr<unwindstack::Regs> regs;
+  std::vector<char> stack;
+};
+
+// Entry in an unwinding queue. Either a sample that requires unwinding, or a
+// tombstoned entry (valid == false).
+struct UnwindEntry {
+  static UnwindEntry Invalid() { return UnwindEntry{}; }
+
+  UnwindEntry() = default;  // for initial unwinding queue entries' state
+
+  UnwindEntry(uint64_t _data_source_id, ParsedSample _sample)
+      : valid(true),
+        data_source_id(_data_source_id),
+        sample(std::move(_sample)) {}
+
+  bool valid = false;
+  uint64_t data_source_id = 0;
+  ParsedSample sample;
+};
+
+// Fully processed sample that is ready for output.
+struct CompletedSample {
+  // move-only
+  CompletedSample() = default;
+  CompletedSample(const CompletedSample&) = delete;
+  CompletedSample& operator=(const CompletedSample&) = delete;
+  CompletedSample(CompletedSample&&) noexcept = default;
+  CompletedSample& operator=(CompletedSample&&) noexcept = default;
+
+  uint16_t cpu_mode = PERF_RECORD_MISC_CPUMODE_UNKNOWN;
+  uint32_t cpu = 0;
+  pid_t pid = 0;
+  pid_t tid = 0;
+  uint64_t timestamp = 0;
+  std::vector<FrameData> frames;
+  unwindstack::ErrorCode unwind_error = unwindstack::ERROR_NONE;
+};
+
+}  // namespace profiling
+}  // namespace perfetto
+
+#endif  // SRC_PROFILING_PERF_COMMON_TYPES_H_
diff --git a/src/profiling/perf/event_config.cc b/src/profiling/perf/event_config.cc
new file mode 100644
index 0000000..eee209f
--- /dev/null
+++ b/src/profiling/perf/event_config.cc
@@ -0,0 +1,191 @@
+/*
+ * 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/profiling/perf/event_config.h"
+
+#include <linux/perf_event.h>
+#include <time.h>
+
+#include <unwindstack/Regs.h>
+
+#include "perfetto/base/flat_set.h"
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/profiling/normalize.h"
+#include "src/profiling/perf/regs_parsing.h"
+
+#include "protos/perfetto/config/profiling/perf_event_config.pbzero.h"
+
+namespace perfetto {
+namespace profiling {
+
+namespace {
+constexpr uint64_t kDefaultSamplingFrequencyHz = 10;
+constexpr uint32_t kDefaultDataPagesPerRingBuffer = 256;  // 1 MB: 256x 4k pages
+constexpr uint32_t kDefaultReadTickPeriodMs = 100;
+constexpr uint32_t kDefaultRemoteDescriptorTimeoutMs = 100;
+
+base::Optional<std::string> Normalize(const protozero::ConstChars& src) {
+  // Construct a null-terminated string that will be mutated by the normalizer.
+  std::vector<char> base(src.size + 1);
+  memcpy(base.data(), src.data, src.size);
+  base[src.size] = '\0';
+
+  char* new_start = base.data();
+  ssize_t new_sz = NormalizeCmdLine(&new_start, base.size());
+  if (new_sz < 0) {
+    PERFETTO_ELOG("Failed to normalize config cmdline [%s], aborting",
+                  base.data());
+    return base::nullopt;
+  }
+  return base::make_optional<std::string>(new_start,
+                                          static_cast<size_t>(new_sz));
+}
+
+// returns |base::nullopt| if any of the input cmdlines couldn't be normalized.
+base::Optional<TargetFilter> ParseTargetFilter(
+    const protos::pbzero::PerfEventConfig::Decoder& cfg) {
+  TargetFilter filter;
+  for (auto it = cfg.target_cmdline(); it; ++it) {
+    base::Optional<std::string> opt = Normalize(*it);
+    if (!opt.has_value())
+      return base::nullopt;
+    filter.cmdlines.insert(std::move(opt.value()));
+  }
+
+  for (auto it = cfg.exclude_cmdline(); it; ++it) {
+    base::Optional<std::string> opt = Normalize(*it);
+    if (!opt.has_value())
+      return base::nullopt;
+    filter.exclude_cmdlines.insert(std::move(opt.value()));
+  }
+
+  for (auto it = cfg.target_pid(); it; ++it) {
+    filter.pids.insert(*it);
+  }
+
+  for (auto it = cfg.exclude_pid(); it; ++it) {
+    filter.exclude_pids.insert(*it);
+  }
+  return base::make_optional(std::move(filter));
+}
+
+constexpr bool IsPowerOfTwo(size_t v) {
+  return (v != 0 && ((v & (v - 1)) == 0));
+}
+
+// returns |base::nullopt| if the input is invalid.
+base::Optional<uint32_t> ChooseActualRingBufferPages(uint32_t config_value) {
+  if (!config_value) {
+    static_assert(IsPowerOfTwo(kDefaultDataPagesPerRingBuffer), "");
+    return base::make_optional(kDefaultDataPagesPerRingBuffer);
+  }
+
+  if (!IsPowerOfTwo(config_value)) {
+    PERFETTO_ELOG("kernel buffer size must be a power of two pages");
+    return base::nullopt;
+  }
+
+  return base::make_optional(config_value);
+}
+
+}  // namespace
+
+// static
+base::Optional<EventConfig> EventConfig::Create(
+    const DataSourceConfig& ds_config) {
+  protos::pbzero::PerfEventConfig::Decoder pb_config(
+      ds_config.perf_event_config_raw());
+
+  base::Optional<TargetFilter> filter = ParseTargetFilter(pb_config);
+  if (!filter.has_value())
+    return base::nullopt;
+
+  base::Optional<uint32_t> ring_buffer_pages =
+      ChooseActualRingBufferPages(pb_config.ring_buffer_pages());
+  if (!ring_buffer_pages.has_value())
+    return base::nullopt;
+
+  uint32_t remote_descriptor_timeout_ms =
+      pb_config.remote_descriptor_timeout_ms()
+          ? pb_config.remote_descriptor_timeout_ms()
+          : kDefaultRemoteDescriptorTimeoutMs;
+
+  uint32_t read_tick_period_ms = pb_config.ring_buffer_read_period_ms()
+                                     ? pb_config.ring_buffer_read_period_ms()
+                                     : kDefaultReadTickPeriodMs;
+
+  uint32_t sampling_frequency = pb_config.sampling_frequency()
+                                    ? pb_config.sampling_frequency()
+                                    : kDefaultSamplingFrequencyHz;
+
+  // Take the ratio of sampling and reading frequencies, which gives the
+  // upper bound on number of samples per tick (for a single per-cpu buffer).
+  // Overflow not a concern for sane inputs.
+  uint32_t expected_samples_per_tick =
+      1 + (sampling_frequency * read_tick_period_ms) / 1000;
+
+  // Use double the expected value as the actual guardrail (don't assume that
+  // periodic read task is as exact as the kernel).
+  uint32_t samples_per_tick_limit = 2 * expected_samples_per_tick;
+  PERFETTO_DCHECK(samples_per_tick_limit > 0);
+  PERFETTO_DLOG("Capping samples (not records) per tick to [%" PRIu32 "]",
+                samples_per_tick_limit);
+
+  return EventConfig(pb_config, sampling_frequency, ring_buffer_pages.value(),
+                     read_tick_period_ms, samples_per_tick_limit,
+                     remote_descriptor_timeout_ms, std::move(filter.value()));
+}
+
+EventConfig::EventConfig(const protos::pbzero::PerfEventConfig::Decoder& cfg,
+                         uint32_t sampling_frequency,
+                         uint32_t ring_buffer_pages,
+                         uint32_t read_tick_period_ms,
+                         uint32_t samples_per_tick_limit,
+                         uint32_t remote_descriptor_timeout_ms,
+                         TargetFilter target_filter)
+    : target_all_cpus_(cfg.all_cpus()),
+      ring_buffer_pages_(ring_buffer_pages),
+      read_tick_period_ms_(read_tick_period_ms),
+      samples_per_tick_limit_(samples_per_tick_limit),
+      target_filter_(std::move(target_filter)),
+      remote_descriptor_timeout_ms_(remote_descriptor_timeout_ms),
+      unwind_state_clear_period_ms_(cfg.unwind_state_clear_period_ms()) {
+  auto& pe = perf_event_attr_;
+  pe.size = sizeof(perf_event_attr);
+
+  pe.disabled = false;
+
+  // Ask the kernel to sample at a given frequency.
+  pe.type = PERF_TYPE_SOFTWARE;
+  pe.config = PERF_COUNT_SW_CPU_CLOCK;
+  pe.freq = true;
+  pe.sample_freq = sampling_frequency;
+
+  pe.sample_type = PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_STACK_USER |
+                   PERF_SAMPLE_REGS_USER;
+  // PERF_SAMPLE_TIME:
+  pe.clockid = CLOCK_BOOTTIME;
+  pe.use_clockid = true;
+  // PERF_SAMPLE_STACK_USER:
+  // Needs to be < ((u16)(~0u)), and have bottom 8 bits clear.
+  pe.sample_stack_user = (1u << 15);
+  // PERF_SAMPLE_REGS_USER:
+  pe.sample_regs_user =
+      PerfUserRegsMaskForArch(unwindstack::Regs::CurrentArch());
+}
+
+}  // namespace profiling
+}  // namespace perfetto
diff --git a/src/profiling/perf/event_config.h b/src/profiling/perf/event_config.h
index cc750ff..27e8915 100644
--- a/src/profiling/perf/event_config.h
+++ b/src/profiling/perf/event_config.h
@@ -17,71 +17,88 @@
 #ifndef SRC_PROFILING_PERF_EVENT_CONFIG_H_
 #define SRC_PROFILING_PERF_EVENT_CONFIG_H_
 
+#include <string>
+
 #include <linux/perf_event.h>
 #include <stdint.h>
 #include <sys/types.h>
 
+#include "perfetto/base/flat_set.h"
 #include "perfetto/ext/base/optional.h"
 #include "perfetto/tracing/core/data_source_config.h"
-#include "src/profiling/perf/unwind_support.h"
 
 #include "protos/perfetto/config/profiling/perf_event_config.pbzero.h"
 
 namespace perfetto {
 namespace profiling {
 
+// Parsed whitelist/blacklist for filtering samples.
+// An empty whitelist set means that all targets are allowed.
+struct TargetFilter {
+  base::FlatSet<std::string> cmdlines;
+  base::FlatSet<std::string> exclude_cmdlines;
+  base::FlatSet<pid_t> pids;
+  base::FlatSet<pid_t> exclude_pids;
+};
+
 // Describes a single profiling configuration. Bridges the gap between the data
 // source config proto, and the raw "perf_event_attr" structs to pass to the
 // perf_event_open syscall.
-// TODO(rsavitski): make sampling conditional? Or should we always go through
-// the sampling interface for simplicity? Reads can be done on-demand even if
-// sampling is on. So the question becomes whether we need *only* on-demand
-// reads.
 class EventConfig {
  public:
-  static base::Optional<EventConfig> Create(const DataSourceConfig& ds_config) {
-    protos::pbzero::PerfEventConfig::Decoder pb_config(
-        ds_config.perf_event_config_raw());
+  static base::Optional<EventConfig> Create(const DataSourceConfig& ds_config);
 
-    return EventConfig(pb_config);
+  uint32_t target_all_cpus() const { return target_all_cpus_; }
+  uint32_t ring_buffer_pages() const { return ring_buffer_pages_; }
+  uint32_t read_tick_period_ms() const { return read_tick_period_ms_; }
+  uint32_t samples_per_tick_limit() const { return samples_per_tick_limit_; }
+  uint32_t remote_descriptor_timeout_ms() const {
+    return remote_descriptor_timeout_ms_;
+  }
+  uint32_t unwind_state_clear_period_ms() const {
+    return unwind_state_clear_period_ms_;
   }
 
-  uint32_t target_cpu() const { return target_cpu_; }
+  const TargetFilter& filter() const { return target_filter_; }
 
   perf_event_attr* perf_attr() const {
     return const_cast<perf_event_attr*>(&perf_event_attr_);
   }
 
  private:
-  EventConfig(const protos::pbzero::PerfEventConfig::Decoder&) {
-    auto& pe = perf_event_attr_;
-    memset(&pe, 0, sizeof(perf_event_attr));
-    pe.size = sizeof(perf_event_attr);
+  EventConfig(const protos::pbzero::PerfEventConfig::Decoder& cfg,
+              uint32_t sampling_frequency,
+              uint32_t ring_buffer_pages,
+              uint32_t read_tick_period_ms,
+              uint32_t samples_per_tick_limit,
+              uint32_t remote_descriptor_timeout_ms,
+              TargetFilter target_filter);
 
-    pe.exclude_kernel = true;
-    pe.disabled = false;
+  // If true, process all system-wide samples.
+  const bool target_all_cpus_;
 
-    // Ask the kernel to tune sampling period to get ~100 Hz.
-    pe.type = PERF_TYPE_SOFTWARE;
-    pe.config = PERF_COUNT_SW_CPU_CLOCK;
-    pe.sample_freq = 100;
-    pe.freq = true;
+  // Size (in 4k pages) of each per-cpu ring buffer shared with the kernel.
+  // Must be a power of two.
+  const uint32_t ring_buffer_pages_;
 
-    pe.sample_type =
-        PERF_SAMPLE_TID | PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER;
-    // Needs to be < ((u16)(~0u)), and have bottom 8 bits clear.
-    pe.sample_stack_user = (1u << 15);
-    pe.sample_regs_user = PerfUserRegsMaskForCurrentArch();
-  }
+  // Parameter struct for |perf_event_open| calls.
+  struct perf_event_attr perf_event_attr_ = {};
 
-  // TODO(rsavitski): for now hardcode each session to be for a single cpu's
-  // scope. In general a config will correspond to N cpus and/or tids.
-  uint32_t target_cpu_ = 0;
+  // How often the ring buffers should be read.
+  const uint32_t read_tick_period_ms_;
 
-  // TODO(rsavitski): if we allow for event groups containing multiple sampled
-  // counters, we'll need to vary the .type & .config fields per
-  // perf_event_open.
-  perf_event_attr perf_event_attr_;
+  // Guardrail for the amount of samples a given read attempt will extract from
+  // *each* per-cpu buffer.
+  const uint32_t samples_per_tick_limit_;
+
+  // Parsed whitelist/blacklist for filtering samples.
+  const TargetFilter target_filter_;
+
+  // Timeout for proc-fd lookup.
+  const uint32_t remote_descriptor_timeout_ms_;
+
+  // Optional period for clearing cached unwinder state. Skipped if zero.
+  const uint32_t unwind_state_clear_period_ms_;
 };
 
 }  // namespace profiling
diff --git a/src/profiling/perf/event_config_unittest.cc b/src/profiling/perf/event_config_unittest.cc
index 0d7fa52..2c69978 100644
--- a/src/profiling/perf/event_config_unittest.cc
+++ b/src/profiling/perf/event_config_unittest.cc
@@ -21,34 +21,107 @@
 
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/optional.h"
-#include "perfetto/protozero/scattered_heap_buffer.h"
-#include "perfetto/tracing/core/data_source_config.h"
 #include "test/gtest_and_gmock.h"
 
-#include "protos/perfetto/config/data_source_config.pbzero.h"
-#include "protos/perfetto/config/profiling/perf_event_config.pbzero.h"
+#include "protos/perfetto/config/data_source_config.gen.h"
+#include "protos/perfetto/config/profiling/perf_event_config.gen.h"
 
 namespace perfetto {
 namespace profiling {
 namespace {
 
-static DataSourceConfig CreateEmptyConfig() {
-  protozero::HeapBuffered<protos::pbzero::PerfEventConfig> pb_config;
-  protozero::HeapBuffered<protos::pbzero::DataSourceConfig> ds_config;
-  ds_config->set_perf_event_config_raw(pb_config.SerializeAsString());
-  DataSourceConfig cfg;
-  PERFETTO_CHECK(cfg.ParseFromString(ds_config.SerializeAsString()));
-  return cfg;
+bool IsPowerOfTwo(size_t v) {
+  return (v != 0 && ((v & (v - 1)) == 0));
+}
+
+static DataSourceConfig AsDataSourceConfig(
+    const protos::gen::PerfEventConfig& perf_cfg) {
+  protos::gen::DataSourceConfig ds_cfg;
+  ds_cfg.set_perf_event_config_raw(perf_cfg.SerializeAsString());
+  return ds_cfg;
 }
 
 TEST(EventConfigTest, AttrStructConstructed) {
-  auto cfg = CreateEmptyConfig();
-  base::Optional<EventConfig> event_config = EventConfig::Create(cfg);
+  protos::gen::PerfEventConfig cfg;
+  base::Optional<EventConfig> event_config =
+      EventConfig::Create(AsDataSourceConfig(cfg));
 
   ASSERT_TRUE(event_config.has_value());
   ASSERT_TRUE(event_config->perf_attr() != nullptr);
 }
 
+TEST(EventConfigTest, RingBufferPagesValidated) {
+  {  // if unset, a default is used
+    protos::gen::PerfEventConfig cfg;
+    base::Optional<EventConfig> event_config =
+        EventConfig::Create(AsDataSourceConfig(cfg));
+
+    ASSERT_TRUE(event_config.has_value());
+    ASSERT_GT(event_config->ring_buffer_pages(), 0u);
+    ASSERT_TRUE(IsPowerOfTwo(event_config->ring_buffer_pages()));
+  }
+  {  // power of two pages accepted
+    uint32_t num_pages = 128;
+    protos::gen::PerfEventConfig cfg;
+    cfg.set_ring_buffer_pages(num_pages);
+    base::Optional<EventConfig> event_config =
+        EventConfig::Create(AsDataSourceConfig(cfg));
+
+    ASSERT_TRUE(event_config.has_value());
+    ASSERT_EQ(event_config->ring_buffer_pages(), num_pages);
+  }
+  {  // entire config rejected if not a power of two of pages
+    protos::gen::PerfEventConfig cfg;
+    cfg.set_ring_buffer_pages(7);
+    base::Optional<EventConfig> event_config =
+        EventConfig::Create(AsDataSourceConfig(cfg));
+
+    ASSERT_FALSE(event_config.has_value());
+  }
+}
+
+TEST(EventConfigTest, ReadTickPeriodDefaultedIfUnset) {
+  {  // if unset, a default is used
+    protos::gen::PerfEventConfig cfg;
+    base::Optional<EventConfig> event_config =
+        EventConfig::Create(AsDataSourceConfig(cfg));
+
+    ASSERT_TRUE(event_config.has_value());
+    ASSERT_GT(event_config->read_tick_period_ms(), 0u);
+  }
+  {  // otherwise, given value used
+    uint32_t period_ms = 250;
+    protos::gen::PerfEventConfig cfg;
+    cfg.set_ring_buffer_read_period_ms(period_ms);
+    base::Optional<EventConfig> event_config =
+        EventConfig::Create(AsDataSourceConfig(cfg));
+
+    ASSERT_TRUE(event_config.has_value());
+    ASSERT_EQ(event_config->read_tick_period_ms(), period_ms);
+  }
+}
+
+TEST(EventConfigTest, RemotePeriodTimeoutDefaultedIfUnset) {
+  {  // if unset, a default is used
+    protos::gen::PerfEventConfig cfg;
+    base::Optional<EventConfig> event_config =
+        EventConfig::Create(AsDataSourceConfig(cfg));
+
+    ASSERT_TRUE(event_config.has_value());
+    ASSERT_GT(event_config->remote_descriptor_timeout_ms(), 0u);
+  }
+  {  // otherwise, given value used
+    uint32_t timeout_ms = 300;
+    protos::gen::PerfEventConfig cfg;
+    cfg.set_remote_descriptor_timeout_ms(timeout_ms);
+    base::Optional<EventConfig> event_config =
+        EventConfig::Create(AsDataSourceConfig(cfg));
+
+    ASSERT_TRUE(event_config.has_value());
+    ASSERT_EQ(event_config->remote_descriptor_timeout_ms(), timeout_ms);
+  }
+}
+
 }  // namespace
 }  // namespace profiling
 }  // namespace perfetto
diff --git a/src/profiling/perf/event_reader.cc b/src/profiling/perf/event_reader.cc
index a2d0e87..b5f8ca2 100644
--- a/src/profiling/perf/event_reader.cc
+++ b/src/profiling/perf/event_reader.cc
@@ -17,13 +17,14 @@
 #include "src/profiling/perf/event_reader.h"
 
 #include <linux/perf_event.h>
+#include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 #include "perfetto/ext/base/utils.h"
-#include "src/profiling/perf/unwind_support.h"
+#include "src/profiling/perf/regs_parsing.h"
 
 namespace perfetto {
 namespace profiling {
@@ -49,14 +50,9 @@
       syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags));
 }
 
-// TODO(rsavitski): one EventConfig will correspond to N perf_event_open calls
-// in the general case. Does it make sense to keep a single function which does
-// the N calls, and then returns the group leader's fd? What about cases where
-// we have >1 pid or >1 cpu to open for? Should the entire EventReader be
-// cpu-scoped?
-base::ScopedFile PerfEventOpen(const EventConfig& event_cfg) {
+base::ScopedFile PerfEventOpen(uint32_t cpu, 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>(cpu),
                       /*group_fd=*/-1, PERF_FLAG_FD_CLOEXEC)};
   return perf_fd;
 }
@@ -95,7 +91,7 @@
     int perf_fd,
     size_t data_page_count) {
   // perf_event_open requires the ring buffer to be a power of two in size.
-  PERFETTO_CHECK(IsPowerOfTwo(data_page_count));
+  PERFETTO_DCHECK(IsPowerOfTwo(data_page_count));
 
   PerfRingBuffer ret;
 
@@ -107,7 +103,7 @@
   void* mmap_addr = mmap(nullptr, ret.mmap_sz_, PROT_READ | PROT_WRITE,
                          MAP_SHARED, perf_fd, 0);
   if (mmap_addr == MAP_FAILED) {
-    PERFETTO_PLOG("failed mmap (check perf_event_mlock_kb in procfs)");
+    PERFETTO_PLOG("failed mmap");
     return base::nullopt;
   }
 
@@ -120,54 +116,87 @@
   return base::make_optional(std::move(ret));
 }
 
-// TODO(rsavitski): look into more specific barrier builtins. Copying simpleperf
-// for now. See |perf_output_put_handle| in the kernel for the barrier
-// requirements.
-#pragma GCC diagnostic push
-#if defined(__clang__)
-#pragma GCC diagnostic ignored "-Watomic-implicit-seq-cst"
-#endif
-std::vector<char> PerfRingBuffer::ReadAvailable() {
-  if (!valid())
-    return {};
+// See |perf_output_put_handle| for the necessary synchronization between the
+// kernel and this userspace thread (which are using the same shared memory, but
+// might be on different cores).
+// TODO(rsavitski): is there false sharing between |data_tail| and |data_head|?
+// Is there an argument for maintaining our own copy of |data_tail| instead of
+// reloading it?
+char* PerfRingBuffer::ReadRecordNonconsuming() {
+  static_assert(sizeof(std::atomic<uint64_t>) == sizeof(uint64_t), "");
 
-  uint64_t write_offset = metadata_page_->data_head;
+  PERFETTO_CHECK(valid());
+
+  // |data_tail| is written only by this userspace thread, so we can safely read
+  // it without any synchronization.
   uint64_t read_offset = metadata_page_->data_tail;
-  __sync_synchronize();  // needs to be rmb()
+
+  // |data_head| is written by the kernel, perform an acquiring load such that
+  // the payload reads below are ordered after this load.
+  uint64_t write_offset =
+      reinterpret_cast<std::atomic<uint64_t>*>(&metadata_page_->data_head)
+          ->load(std::memory_order_acquire);
+
+  PERFETTO_DCHECK(read_offset <= write_offset);
+  if (write_offset == read_offset)
+    return nullptr;  // no new data
 
   size_t read_pos = static_cast<size_t>(read_offset & (data_buf_sz_ - 1));
-  size_t data_sz = static_cast<size_t>(write_offset - read_offset);
 
-  if (data_sz == 0) {
-    return {};
+  // event header (64 bits) guaranteed to be contiguous
+  PERFETTO_DCHECK(read_pos <= data_buf_sz_ - sizeof(perf_event_header));
+  PERFETTO_DCHECK(0 == reinterpret_cast<size_t>(data_buf_ + read_pos) %
+                           alignof(perf_event_header));
+
+  perf_event_header* evt_header =
+      reinterpret_cast<perf_event_header*>(data_buf_ + read_pos);
+  uint16_t evt_size = evt_header->size;
+
+  // event wrapped - reconstruct it, and return a pointer to the buffer
+  if (read_pos + evt_size > data_buf_sz_) {
+    PERFETTO_DCHECK(read_pos + evt_size !=
+                    ((read_pos + evt_size) & (data_buf_sz_ - 1)));
+    PERFETTO_DLOG("PerfRingBuffer: returning reconstructed event");
+
+    size_t prefix_sz = data_buf_sz_ - read_pos;
+    memcpy(&reconstructed_record_[0], data_buf_ + read_pos, prefix_sz);
+    memcpy(&reconstructed_record_[0] + prefix_sz, data_buf_,
+           evt_size - prefix_sz);
+    return &reconstructed_record_[0];
+  } else {
+    // usual case - contiguous sample
+    PERFETTO_DCHECK(read_pos + evt_size ==
+                    ((read_pos + evt_size) & (data_buf_sz_ - 1)));
+
+    return data_buf_ + read_pos;
   }
-
-  // memcpy accounting for wrapping
-  std::vector<char> data(data_sz);
-  size_t copy_sz = std::min(data_sz, data_buf_sz_ - read_pos);
-  memcpy(data.data(), data_buf_ + read_pos, copy_sz);
-  if (copy_sz < data_sz) {
-    memcpy(data.data() + copy_sz, data_buf_, data_sz - copy_sz);
-  }
-
-  // consume the data
-  __sync_synchronize();  // needs to be mb()
-  metadata_page_->data_tail += data_sz;
-
-  PERFETTO_LOG("WIP: consumed [%zu] bytes from ring buffer", data_sz);
-  return data;
 }
-#pragma GCC diagnostic pop
 
-EventReader::EventReader(const EventConfig& event_cfg,
+void PerfRingBuffer::Consume(size_t bytes) {
+  PERFETTO_CHECK(valid());
+
+  // Advance |data_tail|, which is written only by this thread. The store of the
+  // updated value needs to have release semantics such that the preceding
+  // payload reads are ordered before it. The reader in this case is the kernel,
+  // which reads |data_tail| to calculate the available ring buffer capacity
+  // before trying to store a new record.
+  uint64_t updated_tail = metadata_page_->data_tail + bytes;
+  reinterpret_cast<std::atomic<uint64_t>*>(&metadata_page_->data_tail)
+      ->store(updated_tail, std::memory_order_release);
+}
+
+EventReader::EventReader(uint32_t cpu,
+                         perf_event_attr event_attr,
                          base::ScopedFile perf_fd,
                          PerfRingBuffer ring_buffer)
-    : event_cfg_(event_cfg),
+    : cpu_(cpu),
+      event_attr_(event_attr),
       perf_fd_(std::move(perf_fd)),
       ring_buffer_(std::move(ring_buffer)) {}
 
 EventReader::EventReader(EventReader&& other) noexcept
-    : event_cfg_(other.event_cfg_),
+    : cpu_(other.cpu_),
+      event_attr_(other.event_attr_),
       perf_fd_(std::move(other.perf_fd_)),
       ring_buffer_(std::move(other.ring_buffer_)) {}
 
@@ -181,106 +210,139 @@
 }
 
 base::Optional<EventReader> EventReader::ConfigureEvents(
+    uint32_t cpu,
     const EventConfig& event_cfg) {
-  auto perf_fd = PerfEventOpen(event_cfg);
+  auto perf_fd = PerfEventOpen(cpu, event_cfg);
   if (!perf_fd) {
     PERFETTO_PLOG("failed perf_event_open");
     return base::nullopt;
   }
 
   auto ring_buffer =
-      PerfRingBuffer::Allocate(perf_fd.get(), /*data_page_count=*/128);
+      PerfRingBuffer::Allocate(perf_fd.get(), event_cfg.ring_buffer_pages());
   if (!ring_buffer.has_value()) {
     return base::nullopt;
   }
 
-  return base::make_optional<EventReader>(event_cfg, std::move(perf_fd),
+  return base::make_optional<EventReader>(cpu, *event_cfg.perf_attr(),
+                                          std::move(perf_fd),
                                           std::move(ring_buffer.value()));
 }
 
-void EventReader::ParseNextSampleBatch() {
-  std::vector<char> data = ring_buffer_.ReadAvailable();
-  if (data.size() == 0) {
-    PERFETTO_LOG("no samples (work in progress)");
-    return;
-  }
+base::Optional<ParsedSample> EventReader::ReadUntilSample(
+    std::function<void(uint64_t)> records_lost_callback) {
+  for (;;) {
+    char* event = ring_buffer_.ReadRecordNonconsuming();
+    if (!event)
+      return base::nullopt;  // caught up with the writer
 
-  for (const char* ptr = data.data(); ptr < data.data() + data.size();) {
-    if (!ParseSampleAndAdvance(&ptr))
-      break;
+    auto* event_hdr = reinterpret_cast<const perf_event_header*>(event);
+
+    if (event_hdr->type == PERF_RECORD_SAMPLE) {
+      ParsedSample sample = ParseSampleRecord(cpu_, event);
+      ring_buffer_.Consume(event_hdr->size);
+      return base::make_optional(std::move(sample));
+    }
+
+    if (event_hdr->type == PERF_RECORD_LOST) {
+      /*
+       * struct {
+       *   struct perf_event_header header;
+       *   u64 id;
+       *   u64 lost;
+       *   struct sample_id sample_id;
+       * };
+       */
+      uint64_t records_lost = *reinterpret_cast<const uint64_t*>(
+          event + sizeof(perf_event_header) + sizeof(uint64_t));
+
+      records_lost_callback(records_lost);
+      ring_buffer_.Consume(event_hdr->size);
+      continue;  // keep looking for a sample
+    }
+
+    // Kernel had to throttle irqs.
+    if (event_hdr->type == PERF_RECORD_THROTTLE ||
+        event_hdr->type == PERF_RECORD_UNTHROTTLE) {
+      ring_buffer_.Consume(event_hdr->size);
+      continue;  // keep looking for a sample
+    }
+
+    PERFETTO_DFATAL_OR_ELOG("Unsupported event type [%zu]",
+                            static_cast<size_t>(event_hdr->type));
+    ring_buffer_.Consume(event_hdr->size);
   }
 }
 
-bool EventReader::ParseSampleAndAdvance(const char** ptr) {
-  const char* sample_start = *ptr;
-  auto* event_hdr = reinterpret_cast<const perf_event_header*>(sample_start);
-
-  PERFETTO_LOG("WIP: event_header[%zu][%zu][%zu]",
-               static_cast<size_t>(event_hdr->type),
-               static_cast<size_t>(event_hdr->misc),
-               static_cast<size_t>(event_hdr->size));
-
-  if (event_hdr->type == PERF_RECORD_SAMPLE) {
-    ParsePerfRecordSample(sample_start, event_hdr->size);
-  } else {
-    PERFETTO_ELOG("Unsupported event type (work in progress)");
+// Generally, samples can belong to any cpu (which can be recorded with
+// PERF_SAMPLE_CPU). However, this producer uses only cpu-scoped events,
+// therefore it is already known.
+ParsedSample EventReader::ParseSampleRecord(uint32_t cpu,
+                                            const char* record_start) {
+  if (event_attr_.sample_type &
+      (~uint64_t(PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_STACK_USER |
+                 PERF_SAMPLE_REGS_USER))) {
+    PERFETTO_FATAL("Unsupported sampling option");
   }
 
-  *ptr = sample_start + event_hdr->size;
-  return true;
-}
+  auto* event_hdr = reinterpret_cast<const perf_event_header*>(record_start);
+  size_t sample_size = event_hdr->size;
 
-// TODO(rsavitski): actually handle the samples instead of logging.
-void EventReader::ParsePerfRecordSample(const char* sample_start,
-                                        size_t sample_size) {
-  const perf_event_attr* cfg = event_cfg_.perf_attr();
-
-  if (cfg->sample_type & (~uint64_t(PERF_SAMPLE_TID | PERF_SAMPLE_STACK_USER |
-                                    PERF_SAMPLE_REGS_USER))) {
-    PERFETTO_ELOG("Unsupported sampling option (work in progress)");
-    return;
-  }
+  ParsedSample sample = {};
+  sample.cpu = cpu;
+  sample.cpu_mode = event_hdr->misc & PERF_RECORD_MISC_CPUMODE_MASK;
 
   // Parse the payload, which consists of concatenated data for each
   // |attr.sample_type| flag.
-  const char* parse_pos = sample_start + sizeof(perf_event_header);
+  const char* parse_pos = record_start + sizeof(perf_event_header);
 
-  if (cfg->sample_type & PERF_SAMPLE_TID) {
-    uint32_t pid;
+  if (event_attr_.sample_type & PERF_SAMPLE_TID) {
+    uint32_t pid = 0;
+    uint32_t tid = 0;
     parse_pos = ReadValue(&pid, parse_pos);
-    PERFETTO_LOG("pid: %" PRIu32 "", pid);
-
-    uint32_t tid;
     parse_pos = ReadValue(&tid, parse_pos);
-    PERFETTO_LOG("tid: %" PRIu32 "", tid);
+    sample.pid = static_cast<pid_t>(pid);
+    sample.tid = static_cast<pid_t>(tid);
   }
 
-  if (cfg->sample_type & PERF_SAMPLE_REGS_USER) {
-    auto parsed_regs = ReadPerfUserRegsData(&parse_pos);
-
-    if (parsed_regs) {
-      parsed_regs->IterateRegisters([](const char* name, uint64_t value) {
-        PERFETTO_LOG("reg[%s]: %" PRIx64 "", name, value);
-      });
-    }
+  if (event_attr_.sample_type & PERF_SAMPLE_TIME) {
+    parse_pos = ReadValue(&sample.timestamp, parse_pos);
   }
 
-  if (cfg->sample_type & PERF_SAMPLE_STACK_USER) {
+  if (event_attr_.sample_type & PERF_SAMPLE_REGS_USER) {
+    // Can be empty, e.g. if we sampled a kernel thread.
+    sample.regs = ReadPerfUserRegsData(&parse_pos);
+  }
+
+  if (event_attr_.sample_type & PERF_SAMPLE_STACK_USER) {
     uint64_t max_stack_size;  // the requested size
     parse_pos = ReadValue(&max_stack_size, parse_pos);
-    PERFETTO_LOG("max_stack_size: %" PRIu64 "", max_stack_size);
 
-    parse_pos += max_stack_size;  // skip raw data
+    const char* stack_start = parse_pos;
+    parse_pos += max_stack_size;  // skip to dyn_size
 
-    // not written if requested stack sampling size is zero
+    // Payload written conditionally, e.g. kernel threads don't have a
+    // user stack.
     if (max_stack_size > 0) {
       uint64_t filled_stack_size;
       parse_pos = ReadValue(&filled_stack_size, parse_pos);
-      PERFETTO_LOG("filled_stack_size: %" PRIu64 "", filled_stack_size);
+      PERFETTO_DLOG("sampled stack size: %" PRIu64 " / %" PRIu64 "",
+                    filled_stack_size, max_stack_size);
+
+      // copy stack bytes into a vector
+      size_t payload_sz = static_cast<size_t>(filled_stack_size);
+      sample.stack.resize(payload_sz);
+      memcpy(sample.stack.data(), stack_start, payload_sz);
     }
   }
 
-  PERFETTO_CHECK(parse_pos == sample_start + sample_size);
+  PERFETTO_CHECK(parse_pos == record_start + sample_size);
+  return sample;
+}
+
+void EventReader::PauseEvents() {
+  int ret = ioctl(perf_fd_.get(), PERF_EVENT_IOC_DISABLE);
+  PERFETTO_CHECK(ret == 0);
 }
 
 }  // namespace profiling
diff --git a/src/profiling/perf/event_reader.h b/src/profiling/perf/event_reader.h
index fa9b96b..a72e674 100644
--- a/src/profiling/perf/event_reader.h
+++ b/src/profiling/perf/event_reader.h
@@ -24,19 +24,13 @@
 
 #include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/tracing/core/basic_types.h"
+#include "src/profiling/perf/common_types.h"
 #include "src/profiling/perf/event_config.h"
 
 namespace perfetto {
 namespace profiling {
 
-// TODO(rsavitski): currently written for the non-overwriting ring buffer mode
-// (PROT_WRITE). Decide on whether there are use-cases for supporting the other.
-// TODO(rsavitski): given perf_event_mlock_kb limit, can we afford a ring buffer
-// per data source, or will we be forced to multiplex everything onto a single
-// ring buffer in the worst case? Alternatively, obtain CAP_IPC_LOCK (and do own
-// limiting)? Or get an adjusted RLIMIT_MEMLOCK?
-// TODO(rsavitski): polling for now, look into supporting the notification
-// mechanisms (such as epoll) later.
 class PerfRingBuffer {
  public:
   static base::Optional<PerfRingBuffer> Allocate(int perf_fd,
@@ -50,23 +44,29 @@
   PerfRingBuffer(PerfRingBuffer&& other) noexcept;
   PerfRingBuffer& operator=(PerfRingBuffer&& other) noexcept;
 
-  std::vector<char> ReadAvailable();
+  char* ReadRecordNonconsuming();
+  void Consume(size_t bytes);
 
  private:
   PerfRingBuffer() = default;
 
   bool valid() const { return metadata_page_ != nullptr; }
 
-  // TODO(rsavitski): volatile?
-  // Is exactly the start of the mmap'd region.
+  // Points at the start of the mmap'd region.
   perf_event_mmap_page* metadata_page_ = nullptr;
 
-  // size of the mmap'd region (1 metadata page + data_buf_sz_)
+  // Size of the mmap'd region (1 metadata page + data_buf_sz_).
   size_t mmap_sz_ = 0;
 
   // mmap'd ring buffer
   char* data_buf_ = nullptr;
   size_t data_buf_sz_ = 0;
+
+  // When a record wraps around the ring buffer boundary, it is reconstructed in
+  // a contiguous form in this buffer. This allows us to always return a pointer
+  // to a contiguous record.
+  constexpr static size_t kMaxPerfRecordSize = 1 << 16;  // max size 64k
+  alignas(uint64_t) char reconstructed_record_[kMaxPerfRecordSize];
 };
 
 class EventReader {
@@ -76,8 +76,19 @@
   friend struct base::internal::OptionalStorageBase;
 
   static base::Optional<EventReader> ConfigureEvents(
+      uint32_t cpu,
       const EventConfig& event_cfg);
 
+  // Consumes records from the ring buffer until either encountering a sample,
+  // or catching up to the writer. The other record of interest
+  // (PERF_RECORD_LOST) is handled via the given callback.
+  base::Optional<ParsedSample> ReadUntilSample(
+      std::function<void(uint64_t)> lost_events_callback);
+
+  void PauseEvents();
+
+  uint32_t cpu() const { return cpu_; }
+
   ~EventReader() = default;
 
   // move-only
@@ -86,18 +97,17 @@
   EventReader(EventReader&&) noexcept;
   EventReader& operator=(EventReader&&) noexcept;
 
-  // TODO(rsavitski): temporary one-shot parser for development purposes.
-  void ParseNextSampleBatch();
-
  private:
-  EventReader(const EventConfig& event_cfg,
+  EventReader(uint32_t cpu,
+              perf_event_attr event_attr,
               base::ScopedFile perf_fd,
               PerfRingBuffer ring_buffer);
 
-  bool ParseSampleAndAdvance(const char** ptr);
-  void ParsePerfRecordSample(const char* sample_payload, size_t sample_size);
+  ParsedSample ParseSampleRecord(uint32_t cpu, const char* record_start);
 
-  const EventConfig event_cfg_;
+  // All events are cpu-bound (thread-scoped events not supported).
+  const uint32_t cpu_;
+  const perf_event_attr event_attr_;
   base::ScopedFile perf_fd_;
   PerfRingBuffer ring_buffer_;
 };
diff --git a/src/profiling/perf/perf_producer.cc b/src/profiling/perf/perf_producer.cc
index e544f86..e0dde42 100644
--- a/src/profiling/perf/perf_producer.cc
+++ b/src/profiling/perf/perf_producer.cc
@@ -16,11 +16,18 @@
 
 #include "src/profiling/perf/perf_producer.h"
 
-#include <unistd.h>
+#include <random>
 #include <utility>
 
+#include <malloc.h>
+#include <unistd.h>
+
+#include <unwindstack/Error.h>
+#include <unwindstack/Unwinder.h>
+
 #include "perfetto/base/logging.h"
 #include "perfetto/base/task_runner.h"
+#include "perfetto/ext/base/metatrace.h"
 #include "perfetto/ext/base/weak_ptr.h"
 #include "perfetto/ext/tracing/core/basic_types.h"
 #include "perfetto/ext/tracing/core/producer.h"
@@ -28,79 +35,688 @@
 #include "perfetto/ext/tracing/ipc/producer_ipc_client.h"
 #include "perfetto/tracing/core/data_source_config.h"
 #include "perfetto/tracing/core/data_source_descriptor.h"
-#include "protos/perfetto/config/profiling/perf_event_config.pbzero.h"
+#include "src/profiling/common/callstack_trie.h"
+#include "src/profiling/common/proc_utils.h"
+#include "src/profiling/common/unwind_support.h"
+#include "src/profiling/perf/common_types.h"
 #include "src/profiling/perf/event_reader.h"
 
+#include "protos/perfetto/config/profiling/perf_event_config.pbzero.h"
+#include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
 namespace perfetto {
 namespace profiling {
 namespace {
 
+// TODO(b/151835887): on Android, when using signals, there exists a vulnerable
+// window between a process image being replaced by execve, and the new
+// libc instance reinstalling the proper signal handlers. During this window,
+// the signal disposition is defaulted to terminating the process.
+// This is a best-effort mitigation from the daemon's side, using a heuristic
+// that most execve calls follow a fork. So if we get a sample for a very fresh
+// process, the grace period will give it a chance to get to
+// a properly initialised state prior to getting signalled. This doesn't help
+// cases when a mature process calls execve, or when the target gets descheduled
+// (since this is a naive walltime wait).
+// The proper fix is in the platform, see bug for progress.
+constexpr uint32_t kProcDescriptorsAndroidDelayMs = 50;
+
 constexpr uint32_t kInitialConnectionBackoffMs = 100;
 constexpr uint32_t kMaxConnectionBackoffMs = 30 * 1000;
 
 constexpr char kProducerName[] = "perfetto.traced_perf";
 constexpr char kDataSourceName[] = "linux.perf";
 
+size_t NumberOfCpus() {
+  return static_cast<size_t>(sysconf(_SC_NPROCESSORS_CONF));
+}
+
+uint32_t TimeToNextReadTickMs(DataSourceInstanceID ds_id, uint32_t period_ms) {
+  // Normally, we'd schedule the next tick at the next |period_ms|
+  // boundary of the boot clock. However, to avoid aligning the read tasks of
+  // all concurrent data sources, we select a deterministic offset based on the
+  // data source id.
+  std::minstd_rand prng(static_cast<std::minstd_rand::result_type>(ds_id));
+  std::uniform_int_distribution<uint32_t> dist(0, period_ms - 1);
+  uint32_t ds_period_offset = dist(prng);
+
+  uint64_t now_ms = static_cast<uint64_t>(base::GetWallTimeMs().count());
+  return period_ms - ((now_ms - ds_period_offset) % period_ms);
+}
+
+bool ShouldRejectDueToFilter(pid_t pid, const TargetFilter& filter) {
+  bool reject_cmd = false;
+  std::string cmdline;
+  if (GetCmdlineForPID(pid, &cmdline)) {  // normalized form
+    // reject if absent from non-empty whitelist, or present in blacklist
+    reject_cmd = (filter.cmdlines.size() && !filter.cmdlines.count(cmdline)) ||
+                 filter.exclude_cmdlines.count(cmdline);
+  } else {
+    PERFETTO_DLOG("Failed to look up cmdline for pid [%d]",
+                  static_cast<int>(pid));
+    // reject only if there's a whitelist present
+    reject_cmd = filter.cmdlines.size() > 0;
+  }
+
+  bool reject_pid = (filter.pids.size() && !filter.pids.count(pid)) ||
+                    filter.exclude_pids.count(pid);
+
+  if (reject_cmd || reject_pid) {
+    PERFETTO_DLOG(
+        "Rejecting samples for pid [%d] due to cmdline(%d) or pid(%d)",
+        static_cast<int>(pid), reject_cmd, reject_pid);
+
+    return true;
+  }
+  return false;
+}
+
+void MaybeReleaseAllocatorMemToOS() {
+#if defined(__BIONIC__)
+  // TODO(b/152414415): libunwindstack's volume of small allocations is
+  // adverarial to scudo, which doesn't automatically release small
+  // allocation regions back to the OS. Forceful purge does reclaim all size
+  // classes.
+  mallopt(M_PURGE, 0);
+#endif
+}
+
+protos::pbzero::Profiling::CpuMode ToCpuModeEnum(uint16_t perf_cpu_mode) {
+  using Profiling = protos::pbzero::Profiling;
+  switch (perf_cpu_mode) {
+    case PERF_RECORD_MISC_KERNEL:
+      return Profiling::MODE_KERNEL;
+    case PERF_RECORD_MISC_USER:
+      return Profiling::MODE_USER;
+    case PERF_RECORD_MISC_HYPERVISOR:
+      return Profiling::MODE_HYPERVISOR;
+    case PERF_RECORD_MISC_GUEST_KERNEL:
+      return Profiling::MODE_GUEST_KERNEL;
+    case PERF_RECORD_MISC_GUEST_USER:
+      return Profiling::MODE_GUEST_USER;
+    default:
+      return Profiling::MODE_UNKNOWN;
+  }
+}
+
+protos::pbzero::Profiling::StackUnwindError ToProtoEnum(
+    unwindstack::ErrorCode error_code) {
+  using Profiling = protos::pbzero::Profiling;
+  switch (error_code) {
+    case unwindstack::ERROR_NONE:
+      return Profiling::UNWIND_ERROR_NONE;
+    case unwindstack::ERROR_MEMORY_INVALID:
+      return Profiling::UNWIND_ERROR_MEMORY_INVALID;
+    case unwindstack::ERROR_UNWIND_INFO:
+      return Profiling::UNWIND_ERROR_UNWIND_INFO;
+    case unwindstack::ERROR_UNSUPPORTED:
+      return Profiling::UNWIND_ERROR_UNSUPPORTED;
+    case unwindstack::ERROR_INVALID_MAP:
+      return Profiling::UNWIND_ERROR_INVALID_MAP;
+    case unwindstack::ERROR_MAX_FRAMES_EXCEEDED:
+      return Profiling::UNWIND_ERROR_MAX_FRAMES_EXCEEDED;
+    case unwindstack::ERROR_REPEATED_FRAME:
+      return Profiling::UNWIND_ERROR_REPEATED_FRAME;
+    case unwindstack::ERROR_INVALID_ELF:
+      return Profiling::UNWIND_ERROR_INVALID_ELF;
+  }
+  return Profiling::UNWIND_ERROR_UNKNOWN;
+}
+
 }  // namespace
 
-PerfProducer::PerfProducer(base::TaskRunner* task_runner)
-    : task_runner_(task_runner), weak_factory_(this) {}
+PerfProducer::PerfProducer(ProcDescriptorGetter* proc_fd_getter,
+                           base::TaskRunner* task_runner)
+    : task_runner_(task_runner),
+      proc_fd_getter_(proc_fd_getter),
+      unwinding_worker_(this),
+      weak_factory_(this) {
+  proc_fd_getter->SetDelegate(this);
+}
 
-// TODO(rsavitski): configure at setup + enable at start, or do everything on
-// start? Also, do we try to work around the old(?) cpu hotplug bugs as
-// simpleperf does?
+// TODO(rsavitski): consider configure at setup + enable at start instead.
 void PerfProducer::SetupDataSource(DataSourceInstanceID,
                                    const DataSourceConfig&) {}
 
 void PerfProducer::StartDataSource(DataSourceInstanceID instance_id,
                                    const DataSourceConfig& config) {
-  PERFETTO_LOG("StartDataSource(id=%" PRIu64 ", name=%s)", instance_id,
+  PERFETTO_LOG("StartDataSource(%zu, %s)", static_cast<size_t>(instance_id),
                config.name().c_str());
 
+  if (config.name() == MetatraceWriter::kDataSourceName) {
+    StartMetatraceSource(instance_id,
+                         static_cast<BufferID>(config.target_buffer()));
+    return;
+  }
+
+  // linux.perf data source
   if (config.name() != kDataSourceName)
     return;
 
   base::Optional<EventConfig> event_config = EventConfig::Create(config);
   if (!event_config.has_value()) {
-    PERFETTO_LOG("PerfEventConfig rejected.");
+    PERFETTO_ELOG("PerfEventConfig rejected.");
     return;
   }
 
-  base::Optional<EventReader> event_reader =
-      EventReader::ConfigureEvents(event_config.value());
-  if (!event_reader.has_value()) {
-    PERFETTO_LOG("Failed to set up perf events.");
+  // TODO(rsavitski): consider supporting specific cpu subsets.
+  if (!event_config->target_all_cpus()) {
+    PERFETTO_ELOG("PerfEventConfig{all_cpus} required");
     return;
   }
+  size_t num_cpus = NumberOfCpus();
+  std::vector<EventReader> per_cpu_readers;
+  for (uint32_t cpu = 0; cpu < num_cpus; cpu++) {
+    base::Optional<EventReader> event_reader =
+        EventReader::ConfigureEvents(cpu, event_config.value());
+    if (!event_reader.has_value()) {
+      PERFETTO_ELOG("Failed to set up perf events for cpu%" PRIu32
+                    ", discarding data source.",
+                    cpu);
+      return;
+    }
+    per_cpu_readers.emplace_back(std::move(event_reader.value()));
+  }
 
-  // Build the DataSource instance.
-  auto it_inserted = data_sources_.emplace(
+  auto buffer_id = static_cast<BufferID>(config.target_buffer());
+  auto writer = endpoint_->CreateTraceWriter(buffer_id);
+
+  // Construct the data source instance.
+  std::map<DataSourceInstanceID, DataSourceState>::iterator ds_it;
+  bool inserted;
+  std::tie(ds_it, inserted) = data_sources_.emplace(
       std::piecewise_construct, std::forward_as_tuple(instance_id),
-      std::forward_as_tuple(std::move(event_reader.value())));
+      std::forward_as_tuple(event_config.value(), std::move(writer),
+                            std::move(per_cpu_readers)));
+  PERFETTO_CHECK(inserted);
+  DataSourceState& ds = ds_it->second;
 
-  PERFETTO_DCHECK(it_inserted.second);
+  // Write out a packet to initialize the incremental state for this sequence.
+  InterningOutputTracker::WriteFixedInterningsPacket(
+      ds_it->second.trace_writer.get());
+
+  // Inform unwinder of the new data source instance, and optionally start a
+  // periodic task to clear its cached state.
+  unwinding_worker_->PostStartDataSource(instance_id);
+  if (ds.event_config.unwind_state_clear_period_ms()) {
+    unwinding_worker_->PostClearCachedStatePeriodic(
+        instance_id, ds.event_config.unwind_state_clear_period_ms());
+  }
+
+  // Kick off periodic read task.
+  auto tick_period_ms = ds.event_config.read_tick_period_ms();
+  auto weak_this = weak_factory_.GetWeakPtr();
+  task_runner_->PostDelayedTask(
+      [weak_this, instance_id] {
+        if (weak_this)
+          weak_this->TickDataSourceRead(instance_id);
+      },
+      TimeToNextReadTickMs(instance_id, tick_period_ms));
 }
 
 void PerfProducer::StopDataSource(DataSourceInstanceID instance_id) {
-  PERFETTO_LOG("StopDataSource(id=%" PRIu64 ")", instance_id);
+  PERFETTO_LOG("StopDataSource(%zu)", static_cast<size_t>(instance_id));
 
-  data_sources_.erase(instance_id);
+  // Metatrace: stop immediately (will miss the events from the
+  // asynchronous shutdown of the primary data source).
+  auto meta_it = metatrace_writers_.find(instance_id);
+  if (meta_it != metatrace_writers_.end()) {
+    meta_it->second.WriteAllAndFlushTraceWriter([] {});
+    metatrace_writers_.erase(meta_it);
+    return;
+  }
+
+  auto ds_it = data_sources_.find(instance_id);
+  if (ds_it == data_sources_.end())
+    return;
+
+  // Start shutting down the reading frontend, which will propagate the stop
+  // further as the intermediate buffers are cleared.
+  DataSourceState& ds = ds_it->second;
+  InitiateReaderStop(&ds);
 }
 
-void PerfProducer::Flush(FlushRequestID,
+// The perf data sources ignore flush requests, as flushing would be
+// unnecessarily complicated given out-of-order unwinding and proc-fd timeouts.
+// Instead of responding to explicit flushes, we can ensure that we're otherwise
+// well-behaved (do not reorder packets too much), and let the service scrape
+// the SMB.
+void PerfProducer::Flush(FlushRequestID flush_id,
                          const DataSourceInstanceID* data_source_ids,
                          size_t num_data_sources) {
+  bool should_ack_flush = false;
   for (size_t i = 0; i < num_data_sources; i++) {
-    PERFETTO_LOG("Flush(id=%" PRIu64 ")", data_source_ids[i]);
+    auto ds_id = data_source_ids[i];
+    PERFETTO_DLOG("Flush(%zu)", static_cast<size_t>(ds_id));
 
-    auto ds_it = data_sources_.find(data_source_ids[i]);
-    if (ds_it != data_sources_.end()) {
-      auto& ds = ds_it->second;
-
-      // For now, parse whatever's been accumulated in the ring buffer.
-      ds.event_reader.ParseNextSampleBatch();
+    auto meta_it = metatrace_writers_.find(ds_id);
+    if (meta_it != metatrace_writers_.end()) {
+      meta_it->second.WriteAllAndFlushTraceWriter([] {});
+      should_ack_flush = true;
+    }
+    if (data_sources_.find(ds_id) != data_sources_.end()) {
+      should_ack_flush = true;
     }
   }
+  if (should_ack_flush)
+    endpoint_->NotifyFlushComplete(flush_id);
+}
+
+void PerfProducer::ClearIncrementalState(
+    const DataSourceInstanceID* data_source_ids,
+    size_t num_data_sources) {
+  for (size_t i = 0; i < num_data_sources; i++) {
+    auto ds_id = data_source_ids[i];
+    PERFETTO_DLOG("ClearIncrementalState(%zu)", static_cast<size_t>(ds_id));
+
+    if (metatrace_writers_.find(ds_id) != metatrace_writers_.end())
+      continue;
+
+    auto ds_it = data_sources_.find(ds_id);
+    if (ds_it == data_sources_.end()) {
+      PERFETTO_DLOG("ClearIncrementalState(%zu): did not find matching entry",
+                    static_cast<size_t>(ds_id));
+      continue;
+    }
+    DataSourceState& ds = ds_it->second;
+
+    // Forget which incremental state we've emitted before.
+    ds.interning_output.ClearHistory();
+    InterningOutputTracker::WriteFixedInterningsPacket(ds.trace_writer.get());
+
+    // Drop the cross-datasource callstack interning trie. This is not
+    // necessary for correctness (the preceding step is sufficient). However,
+    // incremental clearing is likely to be used in ring buffer traces, where
+    // it makes sense to reset the trie's size periodically, and this is a
+    // reasonable point to do so. The trie keeps the monotonic interning IDs,
+    // so there is no confusion for other concurrent data sources. We do not
+    // bother with clearing concurrent sources' interning output trackers as
+    // their footprint should be trivial.
+    callstack_trie_.ClearTrie();
+  }
+}
+
+void PerfProducer::TickDataSourceRead(DataSourceInstanceID ds_id) {
+  auto it = data_sources_.find(ds_id);
+  if (it == data_sources_.end()) {
+    PERFETTO_DLOG("TickDataSourceRead(%zu): source gone",
+                  static_cast<size_t>(ds_id));
+    return;
+  }
+  DataSourceState& ds = it->second;
+
+  PERFETTO_METATRACE_SCOPED(TAG_PRODUCER, PROFILER_READ_TICK);
+
+  // Make a pass over all per-cpu readers.
+  uint32_t max_samples = ds.event_config.samples_per_tick_limit();
+  bool more_records_available = false;
+  for (EventReader& reader : ds.per_cpu_readers) {
+    if (ReadAndParsePerCpuBuffer(&reader, max_samples, ds_id, &ds)) {
+      more_records_available = true;
+    }
+  }
+
+  // Wake up the unwinder as we've (likely) pushed samples into its queue.
+  unwinding_worker_->PostProcessQueue();
+
+  if (PERFETTO_UNLIKELY(ds.status == DataSourceState::Status::kShuttingDown) &&
+      !more_records_available) {
+    unwinding_worker_->PostInitiateDataSourceStop(ds_id);
+  } else {
+    // otherwise, keep reading
+    auto tick_period_ms = it->second.event_config.read_tick_period_ms();
+    auto weak_this = weak_factory_.GetWeakPtr();
+    task_runner_->PostDelayedTask(
+        [weak_this, ds_id] {
+          if (weak_this)
+            weak_this->TickDataSourceRead(ds_id);
+        },
+        TimeToNextReadTickMs(ds_id, tick_period_ms));
+  }
+}
+
+bool PerfProducer::ReadAndParsePerCpuBuffer(EventReader* reader,
+                                            uint32_t max_samples,
+                                            DataSourceInstanceID ds_id,
+                                            DataSourceState* ds) {
+  PERFETTO_METATRACE_SCOPED(TAG_PRODUCER, PROFILER_READ_CPU);
+
+  // If the kernel ring buffer dropped data, record it in the trace.
+  size_t cpu = reader->cpu();
+  auto records_lost_callback = [this, ds_id, cpu](uint64_t records_lost) {
+    auto weak_this = weak_factory_.GetWeakPtr();
+    task_runner_->PostTask([weak_this, ds_id, cpu, records_lost] {
+      if (weak_this)
+        weak_this->EmitRingBufferLoss(ds_id, cpu, records_lost);
+    });
+  };
+
+  for (uint32_t i = 0; i < max_samples; i++) {
+    base::Optional<ParsedSample> sample =
+        reader->ReadUntilSample(records_lost_callback);
+    if (!sample) {
+      return false;  // caught up to the writer
+    }
+
+    if (!sample->regs) {
+      continue;  // skip kernel threads/workers
+    }
+
+    // Request proc-fds for the process if this is the first time we see it.
+    pid_t pid = sample->pid;
+    auto& process_state = ds->process_states[pid];  // insert if new
+
+    if (process_state == ProcessTrackingStatus::kExpired) {
+      PERFETTO_DLOG("Skipping sample for previously expired pid [%d]",
+                    static_cast<int>(pid));
+      PostEmitSkippedSample(ds_id, std::move(sample.value()),
+                            SampleSkipReason::kReadStage);
+      continue;
+    }
+
+    // Previously failed the target filter check.
+    if (process_state == ProcessTrackingStatus::kRejected) {
+      PERFETTO_DLOG("Skipping sample for pid [%d] due to target filter",
+                    static_cast<int>(pid));
+      continue;
+    }
+
+    // Seeing pid for the first time.
+    if (process_state == ProcessTrackingStatus::kInitial) {
+      PERFETTO_DLOG("New pid: [%d]", static_cast<int>(pid));
+
+      // Check whether samples for this new process should be
+      // dropped due to the target whitelist/blacklist.
+      const TargetFilter& filter = ds->event_config.filter();
+      if (ShouldRejectDueToFilter(pid, filter)) {
+        process_state = ProcessTrackingStatus::kRejected;
+        continue;
+      }
+
+      // At this point, sampled process is known to be of interest, so start
+      // resolving the proc-fds. Response is async.
+      process_state = ProcessTrackingStatus::kResolving;
+      InitiateDescriptorLookup(ds_id, pid,
+                               ds->event_config.remote_descriptor_timeout_ms());
+    }
+
+    PERFETTO_CHECK(process_state == ProcessTrackingStatus::kResolved ||
+                   process_state == ProcessTrackingStatus::kResolving);
+
+    // Push the sample into the unwinding queue if there is room.
+    auto& queue = unwinding_worker_->unwind_queue();
+    WriteView write_view = queue.BeginWrite();
+    if (write_view.valid) {
+      queue.at(write_view.write_pos) =
+          UnwindEntry{ds_id, std::move(sample.value())};
+      queue.CommitWrite();
+    } else {
+      PERFETTO_DLOG("Unwinder queue full, skipping sample");
+      PostEmitSkippedSample(ds_id, std::move(sample.value()),
+                            SampleSkipReason::kUnwindEnqueue);
+    }
+  }
+
+  // Most likely more events in the kernel buffer. Though we might be exactly on
+  // the boundary due to |max_samples|.
+  return true;
+}
+
+// Note: first-fit makes descriptor request fulfillment not true FIFO. But the
+// edge-cases where it matters are very unlikely.
+void PerfProducer::OnProcDescriptors(pid_t pid,
+                                     base::ScopedFile maps_fd,
+                                     base::ScopedFile mem_fd) {
+  // Find first-fit data source that requested descriptors for the process.
+  for (auto& it : data_sources_) {
+    DataSourceState& ds = it.second;
+    auto proc_status_it = ds.process_states.find(pid);
+    if (proc_status_it == ds.process_states.end())
+      continue;
+
+    // Match against either resolving, or expired state. In the latter
+    // case, it means that the async response was slow enough that we've marked
+    // the lookup as expired (but can now recover for future samples).
+    auto proc_status = proc_status_it->second;
+    if (proc_status == ProcessTrackingStatus::kResolving ||
+        proc_status == ProcessTrackingStatus::kExpired) {
+      PERFETTO_DLOG("Handing off proc-fds for pid [%d] to DS [%zu]",
+                    static_cast<int>(pid), static_cast<size_t>(it.first));
+
+      proc_status_it->second = ProcessTrackingStatus::kResolved;
+      unwinding_worker_->PostAdoptProcDescriptors(
+          it.first, pid, std::move(maps_fd), std::move(mem_fd));
+      return;  // done
+    }
+  }
+  PERFETTO_DLOG(
+      "Discarding proc-fds for pid [%d] as found no outstanding requests.",
+      static_cast<int>(pid));
+}
+
+void PerfProducer::InitiateDescriptorLookup(DataSourceInstanceID ds_id,
+                                            pid_t pid,
+                                            uint32_t timeout_ms) {
+  if (!proc_fd_getter_->RequiresDelayedRequest()) {
+    StartDescriptorLookup(ds_id, pid, timeout_ms);
+    return;
+  }
+
+  // Delay lookups on Android. See comment on |kProcDescriptorsAndroidDelayMs|.
+  auto weak_this = weak_factory_.GetWeakPtr();
+  task_runner_->PostDelayedTask(
+      [weak_this, ds_id, pid, timeout_ms] {
+        if (weak_this)
+          weak_this->StartDescriptorLookup(ds_id, pid, timeout_ms);
+      },
+      kProcDescriptorsAndroidDelayMs);
+}
+
+void PerfProducer::StartDescriptorLookup(DataSourceInstanceID ds_id,
+                                         pid_t pid,
+                                         uint32_t timeout_ms) {
+  proc_fd_getter_->GetDescriptorsForPid(pid);
+
+  auto weak_this = weak_factory_.GetWeakPtr();
+  task_runner_->PostDelayedTask(
+      [weak_this, ds_id, pid] {
+        if (weak_this)
+          weak_this->EvaluateDescriptorLookupTimeout(ds_id, pid);
+      },
+      timeout_ms);
+}
+
+void PerfProducer::EvaluateDescriptorLookupTimeout(DataSourceInstanceID ds_id,
+                                                   pid_t pid) {
+  auto ds_it = data_sources_.find(ds_id);
+  if (ds_it == data_sources_.end())
+    return;
+
+  DataSourceState& ds = ds_it->second;
+  auto proc_status_it = ds.process_states.find(pid);
+  if (proc_status_it == ds.process_states.end())
+    return;
+
+  // If the request is still outstanding, mark the process as expired (causing
+  // outstanding and future samples to be discarded).
+  auto proc_status = proc_status_it->second;
+  if (proc_status == ProcessTrackingStatus::kResolving) {
+    PERFETTO_DLOG("Descriptor lookup timeout of pid [%d] for DS [%zu]",
+                  static_cast<int>(pid), static_cast<size_t>(ds_it->first));
+
+    proc_status_it->second = ProcessTrackingStatus::kExpired;
+    // Also inform the unwinder of the state change (so that it can discard any
+    // of the already-enqueued samples).
+    unwinding_worker_->PostRecordTimedOutProcDescriptors(ds_id, pid);
+  }
+}
+
+void PerfProducer::PostEmitSample(DataSourceInstanceID ds_id,
+                                  CompletedSample sample) {
+  // hack: c++11 lambdas can't be moved into, so stash the sample on the heap.
+  CompletedSample* raw_sample = new CompletedSample(std::move(sample));
+  auto weak_this = weak_factory_.GetWeakPtr();
+  task_runner_->PostTask([weak_this, ds_id, raw_sample] {
+    if (weak_this)
+      weak_this->EmitSample(ds_id, std::move(*raw_sample));
+    delete raw_sample;
+  });
+}
+
+void PerfProducer::EmitSample(DataSourceInstanceID ds_id,
+                              CompletedSample sample) {
+  auto ds_it = data_sources_.find(ds_id);
+  PERFETTO_CHECK(ds_it != data_sources_.end());
+  DataSourceState& ds = ds_it->second;
+
+  // intern callsite
+  GlobalCallstackTrie::Node* callstack_root =
+      callstack_trie_.CreateCallsite(sample.frames);
+  uint64_t callstack_iid = callstack_root->id();
+
+  // start packet
+  auto packet = ds.trace_writer->NewTracePacket();
+  packet->set_timestamp(sample.timestamp);
+
+  // write new interning data (if any)
+  protos::pbzero::InternedData* interned_out = packet->set_interned_data();
+  ds.interning_output.WriteCallstack(callstack_root, &callstack_trie_,
+                                     interned_out);
+
+  // write the sample itself
+  auto* perf_sample = packet->set_perf_sample();
+  perf_sample->set_cpu(sample.cpu);
+  perf_sample->set_pid(static_cast<uint32_t>(sample.pid));
+  perf_sample->set_tid(static_cast<uint32_t>(sample.tid));
+  perf_sample->set_cpu_mode(ToCpuModeEnum(sample.cpu_mode));
+  perf_sample->set_callstack_iid(callstack_iid);
+  if (sample.unwind_error != unwindstack::ERROR_NONE) {
+    perf_sample->set_unwind_error(ToProtoEnum(sample.unwind_error));
+  }
+}
+
+void PerfProducer::EmitRingBufferLoss(DataSourceInstanceID ds_id,
+                                      size_t cpu,
+                                      uint64_t records_lost) {
+  auto ds_it = data_sources_.find(ds_id);
+  PERFETTO_CHECK(ds_it != data_sources_.end());
+  DataSourceState& ds = ds_it->second;
+  PERFETTO_DLOG("DataSource(%zu): cpu%zu lost [%" PRIu64 "] records",
+                static_cast<size_t>(ds_id), cpu, records_lost);
+
+  // The data loss record relates to a single ring buffer, and indicates loss
+  // since the last successfully-written record in that buffer. Therefore the
+  // data loss record itself has no timestamp.
+  // We timestamp the packet with the boot clock for packet ordering purposes,
+  // but it no longer has a (precise) interpretation relative to the sample
+  // stream from that per-cpu buffer. See the proto comments for more details.
+  auto packet = ds.trace_writer->NewTracePacket();
+  packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
+
+  auto* perf_sample = packet->set_perf_sample();
+  perf_sample->set_cpu(static_cast<uint32_t>(cpu));
+  perf_sample->set_kernel_records_lost(records_lost);
+}
+
+void PerfProducer::PostEmitUnwinderSkippedSample(DataSourceInstanceID ds_id,
+                                                 ParsedSample sample) {
+  PostEmitSkippedSample(ds_id, std::move(sample),
+                        SampleSkipReason::kUnwindStage);
+}
+
+void PerfProducer::PostEmitSkippedSample(DataSourceInstanceID ds_id,
+                                         ParsedSample sample,
+                                         SampleSkipReason reason) {
+  // hack: c++11 lambdas can't be moved into, so stash the sample on the heap.
+  ParsedSample* raw_sample = new ParsedSample(std::move(sample));
+  auto weak_this = weak_factory_.GetWeakPtr();
+  task_runner_->PostTask([weak_this, ds_id, raw_sample, reason] {
+    if (weak_this)
+      weak_this->EmitSkippedSample(ds_id, std::move(*raw_sample), reason);
+    delete raw_sample;
+  });
+}
+
+void PerfProducer::EmitSkippedSample(DataSourceInstanceID ds_id,
+                                     ParsedSample sample,
+                                     SampleSkipReason reason) {
+  auto ds_it = data_sources_.find(ds_id);
+  PERFETTO_CHECK(ds_it != data_sources_.end());
+  DataSourceState& ds = ds_it->second;
+
+  auto packet = ds.trace_writer->NewTracePacket();
+  packet->set_timestamp(sample.timestamp);
+  auto* perf_sample = packet->set_perf_sample();
+  perf_sample->set_cpu(sample.cpu);
+  perf_sample->set_pid(static_cast<uint32_t>(sample.pid));
+  perf_sample->set_tid(static_cast<uint32_t>(sample.tid));
+  perf_sample->set_cpu_mode(ToCpuModeEnum(sample.cpu_mode));
+
+  using PerfSample = protos::pbzero::PerfSample;
+  switch (reason) {
+    case SampleSkipReason::kReadStage:
+      perf_sample->set_sample_skipped_reason(
+          PerfSample::PROFILER_SKIP_READ_STAGE);
+      break;
+    case SampleSkipReason::kUnwindEnqueue:
+      perf_sample->set_sample_skipped_reason(
+          PerfSample::PROFILER_SKIP_UNWIND_ENQUEUE);
+      break;
+    case SampleSkipReason::kUnwindStage:
+      perf_sample->set_sample_skipped_reason(
+          PerfSample::PROFILER_SKIP_UNWIND_STAGE);
+      break;
+  }
+}
+
+void PerfProducer::InitiateReaderStop(DataSourceState* ds) {
+  PERFETTO_DLOG("InitiateReaderStop");
+  PERFETTO_CHECK(ds->status != DataSourceState::Status::kShuttingDown);
+
+  ds->status = DataSourceState::Status::kShuttingDown;
+  for (auto& event_reader : ds->per_cpu_readers) {
+    event_reader.PauseEvents();
+  }
+}
+
+void PerfProducer::PostFinishDataSourceStop(DataSourceInstanceID ds_id) {
+  auto weak_producer = weak_factory_.GetWeakPtr();
+  task_runner_->PostTask([weak_producer, ds_id] {
+    if (weak_producer)
+      weak_producer->FinishDataSourceStop(ds_id);
+  });
+}
+
+void PerfProducer::FinishDataSourceStop(DataSourceInstanceID ds_id) {
+  PERFETTO_LOG("FinishDataSourceStop(%zu)", static_cast<size_t>(ds_id));
+  auto ds_it = data_sources_.find(ds_id);
+  PERFETTO_CHECK(ds_it != data_sources_.end());
+  DataSourceState& ds = ds_it->second;
+  PERFETTO_CHECK(ds.status == DataSourceState::Status::kShuttingDown);
+
+  ds.trace_writer->Flush();
+  data_sources_.erase(ds_it);
+
+  endpoint_->NotifyDataSourceStopped(ds_id);
+
+  // Clean up resources if there are no more active sources.
+  if (data_sources_.empty()) {
+    callstack_trie_.ClearTrie();  // purge internings
+    MaybeReleaseAllocatorMemToOS();
+  }
+}
+
+void PerfProducer::StartMetatraceSource(DataSourceInstanceID ds_id,
+                                        BufferID target_buffer) {
+  auto writer = endpoint_->CreateTraceWriter(target_buffer);
+
+  auto it_and_inserted = metatrace_writers_.emplace(
+      std::piecewise_construct, std::make_tuple(ds_id), std::make_tuple());
+  PERFETTO_DCHECK(it_and_inserted.second);
+  // Note: only the first concurrent writer will actually be active.
+  metatrace_writers_[ds_id].Enable(task_runner_, std::move(writer),
+                                   metatrace::TAG_ANY);
 }
 
 void PerfProducer::ConnectWithRetries(const char* socket_name) {
@@ -115,8 +731,9 @@
 void PerfProducer::ConnectService() {
   PERFETTO_DCHECK(state_ == kNotConnected);
   state_ = kConnecting;
-  endpoint_ = ProducerIPCClient::Connect(producer_socket_name_, this,
-                                         kProducerName, task_runner_);
+  endpoint_ = ProducerIPCClient::Connect(
+      producer_socket_name_, this, kProducerName, task_runner_,
+      TracingService::ProducerSMBScrapingMode::kEnabled);
 }
 
 void PerfProducer::IncreaseConnectionBackoff() {
@@ -135,9 +752,20 @@
   ResetConnectionBackoff();
   PERFETTO_LOG("Connected to the service");
 
-  DataSourceDescriptor desc;
-  desc.set_name(kDataSourceName);
-  endpoint_->RegisterDataSource(desc);
+  {
+    // linux.perf
+    DataSourceDescriptor desc;
+    desc.set_name(kDataSourceName);
+    desc.set_handles_incremental_state_clear(true);
+    desc.set_will_notify_on_stop(true);
+    endpoint_->RegisterDataSource(desc);
+  }
+  {
+    // metatrace
+    DataSourceDescriptor desc;
+    desc.set_name(MetatraceWriter::kDataSourceName);
+    endpoint_->RegisterDataSource(desc);
+  }
 }
 
 void PerfProducer::OnDisconnect() {
@@ -168,10 +796,11 @@
   // recreate it again.
   base::TaskRunner* task_runner = task_runner_;
   const char* socket_name = producer_socket_name_;
+  ProcDescriptorGetter* proc_fd_getter = proc_fd_getter_;
 
   // Invoke destructor and then the constructor again.
   this->~PerfProducer();
-  new (this) PerfProducer(task_runner);
+  new (this) PerfProducer(proc_fd_getter, task_runner);
 
   ConnectWithRetries(socket_name);
 }
diff --git a/src/profiling/perf/perf_producer.h b/src/profiling/perf/perf_producer.h
index e373836..25fc0d2 100644
--- a/src/profiling/perf/perf_producer.h
+++ b/src/profiling/perf/perf_producer.h
@@ -17,24 +17,48 @@
 #ifndef SRC_PROFILING_PERF_PERF_PRODUCER_H_
 #define SRC_PROFILING_PERF_PERF_PRODUCER_H_
 
+#include <deque>
 #include <map>
+#include <queue>
+
+#include <unistd.h>
+
+#include <unwindstack/Error.h>
+#include <unwindstack/Regs.h>
 
 #include "perfetto/base/task_runner.h"
+#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/base/unix_socket.h"
 #include "perfetto/ext/base/weak_ptr.h"
 #include "perfetto/ext/tracing/core/basic_types.h"
 #include "perfetto/ext/tracing/core/producer.h"
+#include "perfetto/ext/tracing/core/trace_writer.h"
 #include "perfetto/ext/tracing/core/tracing_service.h"
+#include "src/profiling/common/callstack_trie.h"
+#include "src/profiling/common/interning_output.h"
+#include "src/profiling/common/unwind_support.h"
+#include "src/profiling/perf/common_types.h"
 #include "src/profiling/perf/event_config.h"
 #include "src/profiling/perf/event_reader.h"
+#include "src/profiling/perf/proc_descriptors.h"
+#include "src/profiling/perf/unwinding.h"
+#include "src/tracing/core/metatrace_writer.h"
 
 namespace perfetto {
 namespace profiling {
 
-// TODO(b/144281346): work in progress. Do not use.
-class PerfProducer : public Producer {
+// TODO(rsavitski): describe the high-level architecture and threading. Rough
+// summary in the mean time: three stages: (1) kernel buffer reader that parses
+// the samples -> (2) callstack unwinder -> (3) interning and serialization of
+// samples. This class handles stages (1) and (3) on the main thread. Unwinding
+// is done by |Unwinder| on a dedicated thread.
+class PerfProducer : public Producer,
+                     public ProcDescriptorDelegate,
+                     public Unwinder::Delegate {
  public:
-  PerfProducer(base::TaskRunner* task_runner);
+  PerfProducer(ProcDescriptorGetter* proc_fd_getter,
+               base::TaskRunner* task_runner);
   ~PerfProducer() override = default;
 
   PerfProducer(const PerfProducer&) = delete;
@@ -44,21 +68,34 @@
 
   void ConnectWithRetries(const char* socket_name);
 
-  // Producer Impl:
+  // Producer impl:
   void OnConnect() override;
   void OnDisconnect() override;
   void OnTracingSetup() override {}
   void SetupDataSource(DataSourceInstanceID, const DataSourceConfig&) override;
-  void StartDataSource(DataSourceInstanceID, const DataSourceConfig&) override;
-  void StopDataSource(DataSourceInstanceID) override;
-  void Flush(FlushRequestID,
+  void StartDataSource(DataSourceInstanceID instance_id,
+                       const DataSourceConfig& config) override;
+  void StopDataSource(DataSourceInstanceID instance_id) override;
+  void Flush(FlushRequestID flush_id,
              const DataSourceInstanceID* data_source_ids,
              size_t num_data_sources) override;
-  void ClearIncrementalState(const DataSourceInstanceID* /*data_source_ids*/,
-                             size_t /*num_data_sources*/) override {}
+  void ClearIncrementalState(const DataSourceInstanceID* data_source_ids,
+                             size_t num_data_sources) override;
+
+  // ProcDescriptorDelegate impl:
+  void OnProcDescriptors(pid_t pid,
+                         base::ScopedFile maps_fd,
+                         base::ScopedFile mem_fd) override;
+
+  // Unwinder::Delegate impl (callbacks from unwinder):
+  void PostEmitSample(DataSourceInstanceID ds_id,
+                      CompletedSample sample) override;
+  void PostEmitUnwinderSkippedSample(DataSourceInstanceID ds_id,
+                                     ParsedSample sample) override;
+  void PostFinishDataSourceStop(DataSourceInstanceID ds_id) override;
 
  private:
-  // State of the connection to tracing service (traced).
+  // State of the producer's connection to tracing service (traced).
   enum State {
     kNotStarted = 0,
     kNotConnected,
@@ -66,13 +103,48 @@
     kConnected,
   };
 
-  struct DataSource {
-    DataSource(EventReader _event_reader)
-        : event_reader(std::move(_event_reader)) {}
+  // Represents the data source scoped view of a process. Specifically:
+  // * whether the process is in scope of the tracing session (if the latter
+  //   specifies a target filter).
+  // * the state of the (possibly asynchronous) lookup of /proc/<pid>/{maps,mem}
+  //   file descriptors, which are necessary for callstack unwinding of samples.
+  enum class ProcessTrackingStatus {
+    kInitial,
+    kResolving,  // waiting on proc-fd lookup
+    kResolved,   // proc-fds obtained, and process considered relevant
+    kExpired,    // proc-fd lookup timed out
+    kRejected    // process not considered relevant for the data source
+  };
 
-    // TODO(rsavitski): current thinking is an EventReader per cpu-scoped ring
-    // buffer. And a central bookkeeper.
-    EventReader event_reader;
+  struct DataSourceState {
+    enum class Status { kActive, kShuttingDown };
+
+    DataSourceState(EventConfig _event_config,
+                    std::unique_ptr<TraceWriter> _trace_writer,
+                    std::vector<EventReader> _per_cpu_readers)
+        : event_config(std::move(_event_config)),
+          trace_writer(std::move(_trace_writer)),
+          per_cpu_readers(std::move(_per_cpu_readers)) {}
+
+    Status status = Status::kActive;
+    const EventConfig event_config;
+    std::unique_ptr<TraceWriter> trace_writer;
+    // Indexed by cpu, vector never resized.
+    std::vector<EventReader> per_cpu_readers;
+    // Tracks the incremental state for interned entries.
+    InterningOutputTracker interning_output;
+    // Producer thread's view of sampled processes. This is the primary tracking
+    // structure, but a subset of updates are replicated to a similar structure
+    // in the |Unwinder|, which needs to track whether the necessary unwinding
+    // inputs for a given process' samples are ready.
+    std::map<pid_t, ProcessTrackingStatus> process_states;
+  };
+
+  // For |EmitSkippedSample|.
+  enum class SampleSkipReason {
+    kReadStage = 0,  // discarded at read stage
+    kUnwindEnqueue,  // discarded due to unwinder queue being full
+    kUnwindStage,    // discarded at unwind stage
   };
 
   void ConnectService();
@@ -80,13 +152,86 @@
   void ResetConnectionBackoff();
   void IncreaseConnectionBackoff();
 
+  // Periodic read task which reads a batch of samples from all kernel ring
+  // buffers associated with the given data source.
+  void TickDataSourceRead(DataSourceInstanceID ds_id);
+  // Returns *false* if the reader has caught up with the writer position, true
+  // otherwise. Return value is only useful if the underlying perf_event has
+  // been paused (to identify when the buffer is empty). |max_samples| is a cap
+  // on the amount of samples that will be parsed, which might be more than the
+  // number of underlying records (as there might be non-sample records).
+  bool ReadAndParsePerCpuBuffer(EventReader* reader,
+                                uint32_t max_samples,
+                                DataSourceInstanceID ds_id,
+                                DataSourceState* ds);
+
+  void InitiateDescriptorLookup(DataSourceInstanceID ds_id,
+                                pid_t pid,
+                                uint32_t timeout_ms);
+  // Do not call directly, use |InitiateDescriptorLookup|.
+  void StartDescriptorLookup(DataSourceInstanceID ds_id,
+                             pid_t pid,
+                             uint32_t timeout_ms);
+  void EvaluateDescriptorLookupTimeout(DataSourceInstanceID ds_id, pid_t pid);
+
+  void EmitSample(DataSourceInstanceID ds_id, CompletedSample sample);
+  void EmitRingBufferLoss(DataSourceInstanceID ds_id,
+                          size_t cpu,
+                          uint64_t records_lost);
+
+  void PostEmitSkippedSample(DataSourceInstanceID ds_id,
+                             ParsedSample sample,
+                             SampleSkipReason reason);
+  // Emit a packet indicating that a sample was relevant, but skipped as it was
+  // considered to be not unwindable (e.g. the process no longer exists).
+  void EmitSkippedSample(DataSourceInstanceID ds_id,
+                         ParsedSample sample,
+                         SampleSkipReason reason);
+
+  // Starts the shutdown of the given data source instance, starting with
+  // pausing the reader frontend. Once the reader reaches the point where all
+  // kernel buffers have been fully consumed, it will notify the |Unwinder| to
+  // proceed with the shutdown sequence. The unwinder in turn will call back to
+  // this producer once there are no more outstanding samples for the data
+  // source at the unwinding stage.
+  void InitiateReaderStop(DataSourceState* ds);
+  // Destroys the state belonging to this instance, and acks the stop to the
+  // tracing service.
+  void FinishDataSourceStop(DataSourceInstanceID ds_id);
+
+  void StartMetatraceSource(DataSourceInstanceID ds_id, BufferID target_buffer);
+
+  // Task runner owned by the main thread.
   base::TaskRunner* const task_runner_;
   State state_ = kNotStarted;
   const char* producer_socket_name_ = nullptr;
   uint32_t connection_backoff_ms_ = 0;
 
+  // Valid and stable for the lifetime of this class.
+  ProcDescriptorGetter* const proc_fd_getter_;
+
+  // Owns shared memory, must outlive trace writing.
   std::unique_ptr<TracingService::ProducerEndpoint> endpoint_;
-  std::map<DataSourceInstanceID, DataSource> data_sources_;
+
+  // If multiple metatrace sources are enabled concurrently,
+  // only the first one becomes active.
+  std::map<DataSourceInstanceID, MetatraceWriter> metatrace_writers_;
+
+  // Interns callstacks across all data sources.
+  // TODO(rsavitski): for long profiling sessions, consider purging trie when it
+  // grows too large (at the moment purged only when no sources are active).
+  // TODO(rsavitski): interning sequences are monotonic for the lifetime of the
+  // daemon. Consider resetting them at safe points - possible when no sources
+  // are active, and tricky otherwise. In the latter case, it'll require
+  // emitting incremental sequence invalidation packets on all relevant
+  // sequences.
+  GlobalCallstackTrie callstack_trie_;
+
+  // State associated with perf-sampling data sources.
+  std::map<DataSourceInstanceID, DataSourceState> data_sources_;
+
+  // Unwinding stage, running on a dedicated thread.
+  UnwinderHandle unwinding_worker_;
 
   base::WeakPtrFactory<PerfProducer> weak_factory_;  // keep last
 };
diff --git a/src/profiling/perf/proc_descriptors.cc b/src/profiling/perf/proc_descriptors.cc
new file mode 100644
index 0000000..b028d85
--- /dev/null
+++ b/src/profiling/perf/proc_descriptors.cc
@@ -0,0 +1,123 @@
+/*
+ * 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/profiling/perf/proc_descriptors.h"
+
+#include <signal.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+namespace perfetto {
+
+ProcDescriptorDelegate::~ProcDescriptorDelegate() {}
+
+ProcDescriptorGetter::~ProcDescriptorGetter() {}
+
+// DirectDescriptorGetter:
+
+DirectDescriptorGetter::~DirectDescriptorGetter() {}
+
+void DirectDescriptorGetter::SetDelegate(ProcDescriptorDelegate* delegate) {
+  delegate_ = delegate;
+}
+
+void DirectDescriptorGetter::GetDescriptorsForPid(pid_t pid) {
+  char buf[128] = {};
+  snprintf(buf, sizeof(buf), "/proc/%d/maps", pid);
+  auto maps_fd = base::ScopedFile{open(buf, O_RDONLY | O_CLOEXEC)};
+  if (!maps_fd) {
+    if (errno != ENOENT)  // not surprising if the process has quit
+      PERFETTO_PLOG("Failed to open [%s]", buf);
+
+    return;
+  }
+
+  snprintf(buf, sizeof(buf), "/proc/%d/mem", pid);
+  auto mem_fd = base::ScopedFile{open(buf, O_RDONLY | O_CLOEXEC)};
+  if (!mem_fd) {
+    if (errno != ENOENT)  // not surprising if the process has quit
+      PERFETTO_PLOG("Failed to open [%s]", buf);
+
+    return;
+  }
+
+  delegate_->OnProcDescriptors(pid, std::move(maps_fd), std::move(mem_fd));
+}
+
+// AndroidRemoteDescriptorGetter:
+
+AndroidRemoteDescriptorGetter::~AndroidRemoteDescriptorGetter() = default;
+
+void AndroidRemoteDescriptorGetter::SetDelegate(
+    ProcDescriptorDelegate* delegate) {
+  delegate_ = delegate;
+}
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+void AndroidRemoteDescriptorGetter::GetDescriptorsForPid(pid_t) {
+  PERFETTO_FATAL("Unexpected build type for AndroidRemoteDescriptorGetter");
+}
+#else
+void AndroidRemoteDescriptorGetter::GetDescriptorsForPid(pid_t pid) {
+  constexpr static int kPerfProfilerSignalValue = 1;
+  constexpr static int kProfilerSignal = __SIGRTMIN + 4;
+
+  PERFETTO_DLOG("Sending signal to pid [%d]", pid);
+  union sigval signal_value;
+  signal_value.sival_int = kPerfProfilerSignalValue;
+  if (sigqueue(pid, kProfilerSignal, signal_value) != 0 && errno != ESRCH) {
+    PERFETTO_DPLOG("Failed sigqueue(%d)", pid);
+  }
+}
+#endif
+
+void AndroidRemoteDescriptorGetter::OnNewIncomingConnection(
+    base::UnixSocket*,
+    std::unique_ptr<base::UnixSocket> new_connection) {
+  PERFETTO_DLOG("remote fds: new connection from pid [%d]",
+                static_cast<int>(new_connection->peer_pid()));
+
+  active_connections_.emplace(new_connection.get(), std::move(new_connection));
+}
+
+void AndroidRemoteDescriptorGetter::OnDisconnect(base::UnixSocket* self) {
+  PERFETTO_DLOG("remote fds: disconnect from pid [%d]",
+                static_cast<int>(self->peer_pid()));
+
+  auto it = active_connections_.find(self);
+  PERFETTO_CHECK(it != active_connections_.end());
+  active_connections_.erase(it);
+}
+
+// Note: this callback will fire twice for a given connection. Once for the file
+// descriptors, and once during the disconnect (with 0 bytes available in the
+// socket).
+void AndroidRemoteDescriptorGetter::OnDataAvailable(base::UnixSocket* self) {
+  // Expect two file descriptors (maps, followed by mem).
+  base::ScopedFile fds[2];
+  char buf[1];
+  size_t received_bytes =
+      self->Receive(buf, sizeof(buf), fds, base::ArraySize(fds));
+
+  PERFETTO_DLOG("remote fds: received %zu bytes", received_bytes);
+  if (!received_bytes)
+    return;
+
+  delegate_->OnProcDescriptors(self->peer_pid(), std::move(fds[0]),
+                               std::move(fds[1]));
+}
+
+}  // namespace perfetto
diff --git a/src/profiling/perf/proc_descriptors.h b/src/profiling/perf/proc_descriptors.h
new file mode 100644
index 0000000..e524b17
--- /dev/null
+++ b/src/profiling/perf/proc_descriptors.h
@@ -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.
+ */
+
+#ifndef SRC_PROFILING_PERF_PROC_DESCRIPTORS_H_
+#define SRC_PROFILING_PERF_PROC_DESCRIPTORS_H_
+
+#include <sys/types.h>
+
+#include <map>
+
+#include "perfetto/base/task_runner.h"
+#include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/base/unix_socket.h"
+
+namespace perfetto {
+
+// Callback interface for receiving /proc/<pid>/ file descriptors (proc-fds)
+// from |ProcDescriptorGetter|.
+class ProcDescriptorDelegate {
+ public:
+  virtual void OnProcDescriptors(pid_t pid,
+                                 base::ScopedFile maps_fd,
+                                 base::ScopedFile mem_fd) = 0;
+
+  virtual ~ProcDescriptorDelegate();
+};
+
+class ProcDescriptorGetter {
+ public:
+  virtual void GetDescriptorsForPid(pid_t pid) = 0;
+  virtual void SetDelegate(ProcDescriptorDelegate* delegate) = 0;
+  // TODO(b/151835887): attempt to remove the race condition in the Android
+  // platform itself, and remove this best-effort workaround.
+  virtual bool RequiresDelayedRequest() { return false; }
+
+  virtual ~ProcDescriptorGetter();
+};
+
+// Directly opens /proc/<pid>/{maps,mem} files. Used when the daemon is running
+// with sufficient privileges to do so.
+class DirectDescriptorGetter : public ProcDescriptorGetter {
+ public:
+  void GetDescriptorsForPid(pid_t pid) override;
+  void SetDelegate(ProcDescriptorDelegate* delegate) override;
+
+  ~DirectDescriptorGetter() override;
+
+ private:
+  ProcDescriptorDelegate* delegate_ = nullptr;
+};
+
+// Implementation of |ProcDescriptorGetter| used when running as a system daemon
+// on Android. Uses a socket inherited from |init| and platform signal handlers
+// to obtain the proc-fds.
+class AndroidRemoteDescriptorGetter : public ProcDescriptorGetter,
+                                      public base::UnixSocket::EventListener {
+ public:
+  AndroidRemoteDescriptorGetter(int listening_raw_socket,
+                                base::TaskRunner* task_runner) {
+    listening_socket_ = base::UnixSocket::Listen(
+        base::ScopedFile(listening_raw_socket), this, task_runner,
+        base::SockFamily::kUnix, base::SockType::kStream);
+  }
+
+  // ProcDescriptorGetter impl:
+  void GetDescriptorsForPid(pid_t pid) override;
+  void SetDelegate(ProcDescriptorDelegate* delegate) override;
+  bool RequiresDelayedRequest() override { return true; }
+
+  // UnixSocket::EventListener impl:
+  void OnNewIncomingConnection(
+      base::UnixSocket*,
+      std::unique_ptr<base::UnixSocket> new_connection) override;
+  void OnDataAvailable(base::UnixSocket* self) override;
+  void OnDisconnect(base::UnixSocket* self) override;
+
+  ~AndroidRemoteDescriptorGetter() override;
+
+ private:
+  ProcDescriptorDelegate* delegate_ = nullptr;
+  std::unique_ptr<base::UnixSocket> listening_socket_;
+  // Holds onto connections until we receive the file descriptors (keyed by
+  // their raw addresses, which the map keeps stable).
+  std::map<base::UnixSocket*, std::unique_ptr<base::UnixSocket>>
+      active_connections_;
+};
+
+}  // namespace perfetto
+
+#endif  // SRC_PROFILING_PERF_PROC_DESCRIPTORS_H_
diff --git a/src/profiling/perf/regs_parsing.cc b/src/profiling/perf/regs_parsing.cc
new file mode 100644
index 0000000..75c1a75
--- /dev/null
+++ b/src/profiling/perf/regs_parsing.cc
@@ -0,0 +1,260 @@
+/*
+ * 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/profiling/perf/regs_parsing.h"
+
+#include <inttypes.h>
+#include <linux/perf_event.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <memory>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MachineArm.h>
+#include <unwindstack/MachineArm64.h>
+#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>
+
+// kernel uapi 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
+#include <uapi/asm-arm64/asm/perf_regs.h>
+#undef perf_event_arm_regs
+
+namespace perfetto {
+namespace profiling {
+
+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));
+  return ptr + sizeof(T);
+}
+
+// Supported configurations:
+// * 32 bit daemon, 32 bit userspace
+// * 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) {
+  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");
+  }
+}
+
+// Adjusts the given architecture enum based on the ABI (as recorded in the perf
+// sample). Note: we do not support 64 bit samples on a 32 bit daemon build, so
+// this only converts from 64 bit to 32 bit architectures.
+unwindstack::ArchEnum ArchForAbi(unwindstack::ArchEnum arch, uint64_t abi) {
+  if (arch == unwindstack::ARCH_ARM64 && abi == PERF_SAMPLE_REGS_ABI_32) {
+    return unwindstack::ARCH_ARM;
+  }
+  if (arch == unwindstack::ARCH_X86_64 && abi == PERF_SAMPLE_REGS_ABI_32) {
+    return unwindstack::ARCH_X86;
+  }
+  return arch;
+}
+
+// Register values as an array, indexed using the kernel uapi perf_events.h enum
+// values. Unsampled values will be left as zeroes.
+struct RawRegisterData {
+  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) {
+  if (arch == unwindstack::ARCH_ARM64) {
+    static_assert(static_cast<int>(unwindstack::ARM64_REG_R0) ==
+                          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");
+    // 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>(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");
+    // 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));
+  }
+
+  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
+
+uint64_t PerfUserRegsMaskForArch(unwindstack::ArchEnum arch) {
+  return PerfUserRegsMask(arch);
+}
+
+// Assumes that the sampling was configured with
+// |PerfUserRegsMaskForArch(unwindstack::Regs::CurrentArch())|.
+std::unique_ptr<unwindstack::Regs> ReadPerfUserRegsData(const char** data) {
+  unwindstack::ArchEnum requested_arch = unwindstack::Regs::CurrentArch();
+
+  // Layout, assuming a sparse bitmask requesting r1 and r15:
+  // userspace thread: [u64 abi] [u64 r1] [u64 r15]
+  // kernel thread:    [u64 abi]
+  const char* parse_pos = *data;
+  uint64_t sampled_abi;
+  parse_pos = ReadValue(&sampled_abi, parse_pos);
+
+  // ABI_NONE means there were no registers, as we've sampled a kernel thread,
+  // which doesn't have userspace registers.
+  if (sampled_abi == PERF_SAMPLE_REGS_ABI_NONE) {
+    *data = parse_pos;  // adjust caller's parsing position
+    return nullptr;
+  }
+
+  // Unpack the densely-packed register values into |RawRegisterData|, which has
+  // a value for every register (unsampled registers will be left at zero).
+  RawRegisterData raw_regs{};
+  uint64_t regs_mask = PerfUserRegsMaskForArch(requested_arch);
+  for (size_t i = 0; regs_mask && (i < RawRegisterData::kMaxSize); i++) {
+    if (regs_mask & (1ULL << i)) {
+      parse_pos = ReadValue(&raw_regs.regs[i], parse_pos);
+    }
+  }
+
+  // Special case: we've requested arm64 registers from a 64 bit kernel, but
+  // ended up sampling a 32 bit arm userspace process. The 32 bit execution
+  // state of the target process was saved by the exception entry in an
+  // ISA-specific way. The userspace R0-R14 end up saved as arm64 W0-W14, but
+  // the program counter (R15 on arm32) is still in PERF_REG_ARM64_PC (the 33rd
+  // register). So we can take the kernel-dumped 64 bit register state, reassign
+  // 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];
+  }
+
+  *data = parse_pos;  // adjust caller's parsing position
+
+  unwindstack::ArchEnum sampled_arch = ArchForAbi(requested_arch, sampled_abi);
+  return ToLibUnwindstackRegs(raw_regs, sampled_arch);
+}
+
+}  // namespace profiling
+}  // namespace perfetto
diff --git a/src/profiling/perf/unwind_support.h b/src/profiling/perf/regs_parsing.h
similarity index 87%
rename from src/profiling/perf/unwind_support.h
rename to src/profiling/perf/regs_parsing.h
index f0764d2..6dc86dd 100644
--- a/src/profiling/perf/unwind_support.h
+++ b/src/profiling/perf/regs_parsing.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_PROFILING_PERF_UNWIND_SUPPORT_H_
-#define SRC_PROFILING_PERF_UNWIND_SUPPORT_H_
+#ifndef SRC_PROFILING_PERF_REGS_PARSING_H_
+#define SRC_PROFILING_PERF_REGS_PARSING_H_
 
 #include <stdint.h>
 #include <unwindstack/Regs.h>
@@ -29,7 +29,7 @@
 
 // Returns a bitmask for sampling the userspace register set, used when
 // configuring perf events.
-uint64_t PerfUserRegsMaskForCurrentArch();
+uint64_t PerfUserRegsMaskForArch(unwindstack::ArchEnum arch);
 
 // Converts the raw sampled register bytes to libunwindstack's representation
 // (correct arch-dependent subclass). Advances |data| pointer to past the
@@ -42,4 +42,4 @@
 }  // namespace profiling
 }  // namespace perfetto
 
-#endif  // SRC_PROFILING_PERF_UNWIND_SUPPORT_H_
+#endif  // SRC_PROFILING_PERF_REGS_PARSING_H_
diff --git a/src/profiling/perf/traced_perf.cc b/src/profiling/perf/traced_perf.cc
index c165b20..77ce7c4 100644
--- a/src/profiling/perf/traced_perf.cc
+++ b/src/profiling/perf/traced_perf.cc
@@ -18,13 +18,40 @@
 #include "perfetto/ext/base/unix_task_runner.h"
 #include "perfetto/ext/tracing/ipc/default_socket.h"
 #include "src/profiling/perf/perf_producer.h"
+#include "src/profiling/perf/proc_descriptors.h"
 
 namespace perfetto {
 
+namespace {
+#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+static constexpr char kTracedPerfSocketEnvVar[] = "ANDROID_SOCKET_traced_perf";
+
+int GetRawInheritedListeningSocket() {
+  const char* sock_fd = getenv(kTracedPerfSocketEnvVar);
+  if (sock_fd == nullptr)
+    PERFETTO_FATAL("Did not inherit socket from init.");
+  char* end;
+  int raw_fd = static_cast<int>(strtol(sock_fd, &end, 10));
+  if (*end != '\0')
+    PERFETTO_FATAL("Invalid env variable format. Expected decimal integer.");
+  return raw_fd;
+}
+#endif
+}  // namespace
+
 // TODO(rsavitski): watchdog.
 int TracedPerfMain(int, char**) {
   base::UnixTaskRunner task_runner;
-  profiling::PerfProducer producer(&task_runner);
+
+// TODO(rsavitski): support standalone --root or similar on android.
+#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+  AndroidRemoteDescriptorGetter proc_fd_getter{GetRawInheritedListeningSocket(),
+                                               &task_runner};
+#else
+  DirectDescriptorGetter proc_fd_getter;
+#endif
+
+  profiling::PerfProducer producer(&proc_fd_getter, &task_runner);
   producer.ConnectWithRetries(GetProducerSocket());
   task_runner.Run();
   return 0;
diff --git a/src/profiling/perf/unwind_queue.h b/src/profiling/perf/unwind_queue.h
new file mode 100644
index 0000000..b49b176
--- /dev/null
+++ b/src/profiling/perf/unwind_queue.h
@@ -0,0 +1,94 @@
+/*
+ * 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_PROFILING_PERF_UNWIND_QUEUE_H_
+#define SRC_PROFILING_PERF_UNWIND_QUEUE_H_
+
+#include <array>
+#include <atomic>
+
+#include "perfetto/base/logging.h"
+
+namespace perfetto {
+namespace profiling {
+
+struct WriteView {
+  bool valid;  // if false: buffer is full, and |write_pos| is invalid
+  uint64_t write_pos;
+};
+
+struct ReadView {
+  uint64_t read_pos;
+  uint64_t write_pos;
+};
+
+// Single-writer, single-reader ring buffer of fixed-size entries (of any
+// default-constructible type). Size of the buffer is static for the lifetime of
+// UnwindQueue, and must be a power of two.
+// Writer side appends entries one at a time, and must stop if there
+// is no available capacity.
+// Reader side sees all unconsumed entries, and can advance the reader position
+// by any amount.
+template <typename T, uint32_t QueueSize>
+class UnwindQueue {
+ public:
+  UnwindQueue() {
+    static_assert(QueueSize != 0 && ((QueueSize & (QueueSize - 1)) == 0),
+                  "not a power of two");
+  }
+
+  UnwindQueue(const UnwindQueue&) = delete;
+  UnwindQueue& operator=(const UnwindQueue&) = delete;
+  UnwindQueue(UnwindQueue&&) = delete;
+  UnwindQueue& operator=(UnwindQueue&&) = delete;
+
+  T& at(uint64_t pos) { return data_[pos % QueueSize]; }
+
+  WriteView BeginWrite() {
+    uint64_t rd = rd_pos_.load(std::memory_order_acquire);
+    uint64_t wr = wr_pos_.load(std::memory_order_relaxed);
+
+    PERFETTO_DCHECK(wr >= rd);
+    if (wr - rd >= QueueSize)
+      return WriteView{false, 0};  // buffer fully occupied
+
+    return WriteView{true, wr};
+  }
+
+  void CommitWrite() { wr_pos_.fetch_add(1u, std::memory_order_release); }
+
+  ReadView BeginRead() {
+    uint64_t wr = wr_pos_.load(std::memory_order_acquire);
+    uint64_t rd = rd_pos_.load(std::memory_order_relaxed);
+
+    PERFETTO_DCHECK(wr >= rd && wr - rd <= QueueSize);
+    return ReadView{rd, wr};
+  }
+
+  void CommitNewReadPosition(uint64_t pos) {
+    rd_pos_.store(pos, std::memory_order_release);
+  }
+
+ private:
+  std::array<T, QueueSize> data_;
+  std::atomic<uint64_t> wr_pos_{0};
+  std::atomic<uint64_t> rd_pos_{0};
+};
+
+}  // namespace profiling
+}  // namespace perfetto
+
+#endif  // SRC_PROFILING_PERF_UNWIND_QUEUE_H_
diff --git a/src/profiling/perf/unwind_queue_unittest.cc b/src/profiling/perf/unwind_queue_unittest.cc
new file mode 100644
index 0000000..e906d59
--- /dev/null
+++ b/src/profiling/perf/unwind_queue_unittest.cc
@@ -0,0 +1,116 @@
+/*
+ * 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/profiling/perf/unwind_queue.h"
+
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace profiling {
+namespace {
+
+TEST(UnwindQueueTest, SinglePass) {
+  static constexpr uint32_t kCapacity = 4;
+  UnwindQueue<int, kCapacity> queue;
+
+  // write kCapacity entries
+  for (int i = 0; i < static_cast<int>(kCapacity); i++) {
+    WriteView v = queue.BeginWrite();
+    ASSERT_TRUE(v.valid);
+    queue.at(v.write_pos) = i;
+    queue.CommitWrite();
+  }
+  {
+    // no more available capacity
+    WriteView v = queue.BeginWrite();
+    ASSERT_FALSE(v.valid);
+  }
+
+  // reader sees all four writes
+  ReadView v = queue.BeginRead();
+  ASSERT_EQ(v.read_pos, 0u);
+  ASSERT_EQ(v.write_pos, 4u);
+
+  std::vector<int> read_back;
+  for (auto pos = v.read_pos; pos < v.write_pos; pos++) {
+    read_back.push_back(queue.at(pos));
+  }
+  queue.CommitNewReadPosition(v.write_pos);
+
+  ASSERT_THAT(read_back, ::testing::ElementsAre(0, 1, 2, 3));
+
+  // writer sees an available slot
+  ASSERT_TRUE(queue.BeginWrite().valid);
+  // reader caught up
+  ASSERT_TRUE(queue.BeginRead().read_pos == queue.BeginRead().write_pos);
+}
+
+TEST(UnwindQueueTest, Wrapped) {
+  static constexpr uint32_t kCapacity = 4;
+  UnwindQueue<int, kCapacity> queue;
+
+  // write kCapacity entries
+  for (int i = 0; i < static_cast<int>(kCapacity); i++) {
+    WriteView v = queue.BeginWrite();
+    ASSERT_TRUE(v.valid);
+    queue.at(v.write_pos) = i;
+    queue.CommitWrite();
+  }
+
+  // no more available capacity
+  ASSERT_FALSE(queue.BeginWrite().valid);
+
+  {
+    // consume 2 entries (partial read)
+    ReadView v = queue.BeginRead();
+    ASSERT_EQ(v.read_pos, 0u);
+    ASSERT_EQ(v.write_pos, 4u);
+    queue.CommitNewReadPosition(v.read_pos + 2);
+  }
+
+  // write 2 more entries
+  for (int i = 0; i < 2; i++) {
+    WriteView v = queue.BeginWrite();
+    ASSERT_TRUE(v.valid);
+    queue.at(v.write_pos) = 4 + i;
+    queue.CommitWrite();
+  }
+
+  // no more available capacity
+  ASSERT_FALSE(queue.BeginWrite().valid);
+
+  // read the remainder of the buffer
+  ReadView v = queue.BeginRead();
+  ASSERT_EQ(v.read_pos, 2u);
+  ASSERT_EQ(v.write_pos, 6u);
+
+  std::vector<int> read_back;
+  for (auto pos = v.read_pos; pos < v.write_pos; pos++) {
+    read_back.push_back(queue.at(pos));
+  }
+  queue.CommitNewReadPosition(v.write_pos);
+
+  ASSERT_THAT(read_back, ::testing::ElementsAre(2, 3, 4, 5));
+
+  // writer sees an available slot
+  ASSERT_TRUE(queue.BeginWrite().valid);
+  // reader caught up
+  ASSERT_TRUE(queue.BeginRead().read_pos == queue.BeginRead().write_pos);
+}
+
+}  // namespace
+}  // namespace profiling
+}  // namespace perfetto
diff --git a/src/profiling/perf/unwind_support.cc b/src/profiling/perf/unwind_support.cc
deleted file mode 100644
index 47dc3c0..0000000
--- a/src/profiling/perf/unwind_support.cc
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * 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/profiling/perf/unwind_support.h"
-
-#include <inttypes.h>
-#include <linux/perf_event.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <memory>
-
-#include <unwindstack/Elf.h>
-#include <unwindstack/MachineArm.h>
-#include <unwindstack/MachineArm64.h>
-#include <unwindstack/Regs.h>
-#include <unwindstack/RegsArm.h>
-#include <unwindstack/RegsArm64.h>
-#include <unwindstack/UserArm.h>
-#include <unwindstack/UserArm64.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
-#include <uapi/asm-arm64/asm/perf_regs.h>
-#undef perf_event_arm_regs
-
-namespace perfetto {
-namespace profiling {
-
-namespace {
-
-template <typename T>
-const char* ReadValue(T* value_out, const char* ptr) {
-  memcpy(value_out, reinterpret_cast<const void*>(ptr), sizeof(T));
-  return ptr + sizeof(T);
-}
-
-// Supported configurations:
-// * 32 bit daemon, 32 bit userspace
-// * 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.
-// 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) {
-    case unwindstack::ARCH_ARM64:
-      return (1ULL << PERF_REG_ARM64_MAX) - 1;
-    case unwindstack::ARCH_ARM:
-      return ((1ULL << PERF_REG_ARM_MAX) - 1);
-    default:
-      PERFETTO_FATAL("Unsupported architecture (work in progress)");
-  }
-}
-
-// Adjusts the given architecture enum based on the ABI (as recorded in the perf
-// sample). Note: we do not support 64 bit samples on a 32 bit daemon build, so
-// this only converts from 64 bit to 32 bit architectures.
-unwindstack::ArchEnum ArchForAbi(unwindstack::ArchEnum arch, uint64_t abi) {
-  if (arch == unwindstack::ARCH_ARM64 && abi == PERF_SAMPLE_REGS_ABI_32) {
-    return unwindstack::ARCH_ARM;
-  }
-  if (arch == unwindstack::ARCH_X86_64 && abi == PERF_SAMPLE_REGS_ABI_32) {
-    return unwindstack::ARCH_X86;
-  }
-  return arch;
-}
-
-// 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;
-  uint64_t regs[kMaxSize] = {};
-};
-
-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),
-                  "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));
-    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),
-                  "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++) {
-      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)");
-}
-
-}  // namespace
-
-uint64_t PerfUserRegsMaskForCurrentArch() {
-  return PerfUserRegsMask(unwindstack::Regs::CurrentArch());
-}
-
-// Assumes that the sampling was configured with
-// |PerfUserRegsMaskForCurrentArch|.
-std::unique_ptr<unwindstack::Regs> ReadPerfUserRegsData(const char** data) {
-  unwindstack::ArchEnum requested_arch = unwindstack::Regs::CurrentArch();
-
-  // Layout, assuming a sparse bitmask requesting r1 and r15:
-  // [u64 abi] [u64 r1] [u64 r15]
-  const char* parse_pos = *data;
-  uint64_t sampled_abi;
-  parse_pos = ReadValue(&sampled_abi, parse_pos);
-  PERFETTO_LOG("WIP: abi: %" PRIu64 "", sampled_abi);
-
-  // Unpack the densely-packed register values into |RawRegisterData|, which has
-  // a value for every register (unsampled registers will be left at zero).
-  RawRegisterData raw_regs{};
-  uint64_t regs_mask = PerfUserRegsMaskForCurrentArch();
-  for (size_t i = 0; regs_mask && (i < RawRegisterData::kMaxSize); i++) {
-    if (regs_mask & (1u << i)) {
-      parse_pos = ReadValue(&raw_regs.regs[i], parse_pos);
-    }
-  }
-
-  // Special case: we've requested arm64 registers from a 64 bit kernel, but
-  // ended up sampling a 32 bit arm userspace process. The 32 bit execution
-  // state of the target process was saved by the exception entry in an
-  // ISA-specific way. The userspace R0-R14 end up saved as arm64 W0-W14, but
-  // the program counter (R15 on arm32) is still in PERF_REG_ARM64_PC (the 33rd
-  // register). So we can take the kernel-dumped 64 bit register state, reassign
-  // 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.
-  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];
-  }
-
-  // Adjust caller's parsing position.
-  *data = parse_pos;
-
-  // ABI_NONE means there were no registers (e.g. we've sampled a kernel thread,
-  // which doesn't have userspace registers). We still walk over the empty data
-  // above, but return an empty result to the caller.
-  if (sampled_abi == PERF_SAMPLE_REGS_ABI_NONE) {
-    return nullptr;
-  } else {
-    unwindstack::ArchEnum sampled_arch =
-        ArchForAbi(requested_arch, sampled_abi);
-    return ToLibUnwindstackRegs(raw_regs, sampled_arch);
-  }
-}
-
-}  // namespace profiling
-}  // namespace perfetto
diff --git a/src/profiling/perf/unwinding.cc b/src/profiling/perf/unwinding.cc
new file mode 100644
index 0000000..6457422
--- /dev/null
+++ b/src/profiling/perf/unwinding.cc
@@ -0,0 +1,450 @@
+/*
+ * 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/profiling/perf/unwinding.h"
+
+#include <mutex>
+
+#include <inttypes.h>
+
+#include "perfetto/ext/base/metatrace.h"
+#include "perfetto/ext/base/thread_utils.h"
+
+namespace {
+constexpr size_t kUnwindingMaxFrames = 1000;
+constexpr uint32_t kDataSourceShutdownRetryDelayMs = 400;
+
+void MaybeReleaseAllocatorMemToOS() {
+#if defined(__BIONIC__)
+  // TODO(b/152414415): libunwindstack's volume of small allocations is
+  // adverarial to scudo, which doesn't automatically release small
+  // allocation regions back to the OS. Forceful purge does reclaim all size
+  // classes.
+  mallopt(M_PURGE, 0);
+#endif
+}
+
+}  // namespace
+
+namespace perfetto {
+namespace profiling {
+
+Unwinder::Delegate::~Delegate() = default;
+
+Unwinder::Unwinder(Delegate* delegate, base::UnixTaskRunner* task_runner)
+    : task_runner_(task_runner), delegate_(delegate) {
+  ResetAndEnableUnwindstackCache();
+  base::MaybeSetThreadName("stack-unwinding");
+}
+
+void Unwinder::PostStartDataSource(DataSourceInstanceID ds_id) {
+  // No need for a weak pointer as the associated task runner quits (stops
+  // running tasks) strictly before the Unwinder's destruction.
+  task_runner_->PostTask([this, ds_id] { StartDataSource(ds_id); });
+}
+
+void Unwinder::StartDataSource(DataSourceInstanceID ds_id) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  PERFETTO_DLOG("Unwinder::StartDataSource(%zu)", static_cast<size_t>(ds_id));
+
+  auto it_and_inserted = data_sources_.emplace(ds_id, DataSourceState{});
+  PERFETTO_DCHECK(it_and_inserted.second);
+}
+
+// c++11: use shared_ptr to transfer resource handles, so that the resources get
+// released even if the task runner is destroyed with pending tasks.
+// "Cleverness" warning:
+// the task will be executed on a different thread, and will mutate the
+// pointed-to memory. It may be the case that this posting thread will not
+// decrement its shared_ptr refcount until *after* the task has executed. In
+// that scenario, the destruction of the pointed-to memory will be happening on
+// the posting thread. This implies a data race between the mutation on the task
+// thread, and the destruction on the posting thread. *However*, we assume that
+// there is no race in practice due to refcount decrements having
+// release-acquire semantics. The refcount decrements pair with each other, and
+// therefore also serve as a memory barrier between the destructor, and any
+// previous modifications of the pointed-to memory.
+// TODO(rsavitski): present a more convincing argument, or reimplement
+// without relying on shared_ptr implementation details.
+void Unwinder::PostAdoptProcDescriptors(DataSourceInstanceID ds_id,
+                                        pid_t pid,
+                                        base::ScopedFile maps_fd,
+                                        base::ScopedFile mem_fd) {
+  auto shared_maps = std::make_shared<base::ScopedFile>(std::move(maps_fd));
+  auto shared_mem = std::make_shared<base::ScopedFile>(std::move(mem_fd));
+  task_runner_->PostTask([this, ds_id, pid, shared_maps, shared_mem] {
+    base::ScopedFile maps = std::move(*shared_maps.get());
+    base::ScopedFile mem = std::move(*shared_mem.get());
+    AdoptProcDescriptors(ds_id, pid, std::move(maps), std::move(mem));
+  });
+}
+
+void Unwinder::AdoptProcDescriptors(DataSourceInstanceID ds_id,
+                                    pid_t pid,
+                                    base::ScopedFile maps_fd,
+                                    base::ScopedFile mem_fd) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  PERFETTO_DLOG("Unwinder::AdoptProcDescriptors(%zu, %d, %d, %d)",
+                static_cast<size_t>(ds_id), static_cast<int>(pid),
+                maps_fd.get(), mem_fd.get());
+
+  auto it = data_sources_.find(ds_id);
+  PERFETTO_CHECK(it != data_sources_.end());
+  DataSourceState& ds = it->second;
+
+  ProcessState& proc_state = ds.process_states[pid];  // insert if new
+  PERFETTO_DCHECK(proc_state.status != ProcessState::Status::kResolved);
+  PERFETTO_DCHECK(!proc_state.unwind_state.has_value());
+
+  PERFETTO_METATRACE_SCOPED(TAG_PRODUCER, PROFILER_MAPS_PARSE);
+
+  proc_state.status = ProcessState::Status::kResolved;
+  proc_state.unwind_state =
+      UnwindingMetadata{std::move(maps_fd), std::move(mem_fd)};
+}
+
+void Unwinder::PostRecordTimedOutProcDescriptors(DataSourceInstanceID ds_id,
+                                                 pid_t pid) {
+  task_runner_->PostTask(
+      [this, ds_id, pid] { RecordTimedOutProcDescriptors(ds_id, pid); });
+}
+
+void Unwinder::RecordTimedOutProcDescriptors(DataSourceInstanceID ds_id,
+                                             pid_t pid) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  PERFETTO_DLOG("Unwinder::RecordTimedOutProcDescriptors(%zu, %d)",
+                static_cast<size_t>(ds_id), static_cast<int>(pid));
+
+  auto it = data_sources_.find(ds_id);
+  PERFETTO_CHECK(it != data_sources_.end());
+  DataSourceState& ds = it->second;
+
+  ProcessState& proc_state = ds.process_states[pid];  // insert if new
+  PERFETTO_DCHECK(proc_state.status == ProcessState::Status::kResolving);
+  PERFETTO_DCHECK(!proc_state.unwind_state.has_value());
+
+  proc_state.status = ProcessState::Status::kExpired;
+}
+
+void Unwinder::PostProcessQueue() {
+  task_runner_->PostTask([this] { ProcessQueue(); });
+}
+
+// Note: we always walk the queue in order. So if there are multiple data
+// sources, one of which is shutting down, its shutdown can be delayed by
+// unwinding of other sources' samples. Instead, we could scan the queue
+// multiple times, prioritizing the samples for shutting-down sources. At the
+// time of writing, the earlier is considered to be fair enough.
+void Unwinder::ProcessQueue() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  PERFETTO_METATRACE_SCOPED(TAG_PRODUCER, PROFILER_UNWIND_TICK);
+  PERFETTO_DLOG("Unwinder::ProcessQueue");
+
+  base::FlatSet<DataSourceInstanceID> pending_sample_sources =
+      ConsumeAndUnwindReadySamples();
+
+  // Deal with the possiblity of data sources that are shutting down.
+  bool post_delayed_reprocess = false;
+  base::FlatSet<DataSourceInstanceID> sources_to_stop;
+  for (auto& id_and_ds : data_sources_) {
+    DataSourceInstanceID ds_id = id_and_ds.first;
+    const DataSourceState& ds = id_and_ds.second;
+
+    if (ds.status == DataSourceState::Status::kActive)
+      continue;
+
+    // Data source that is shutting down. If we're still waiting on proc-fds (or
+    // the lookup to time out) for samples in the queue - repost a later
+    // attempt (as there is no guarantee that there are any readers waking up
+    // the unwinder anymore).
+    if (pending_sample_sources.count(ds_id)) {
+      PERFETTO_DLOG(
+          "Unwinder delaying DS(%zu) stop: waiting on a pending sample",
+          static_cast<size_t>(ds_id));
+      post_delayed_reprocess = true;
+    } else {
+      // Otherwise, proceed with tearing down data source state (after
+      // completing the loop, to avoid invalidating the iterator).
+      sources_to_stop.insert(ds_id);
+    }
+  }
+
+  for (auto ds_id : sources_to_stop)
+    FinishDataSourceStop(ds_id);
+
+  if (post_delayed_reprocess)
+    task_runner_->PostDelayedTask([this] { ProcessQueue(); },
+                                  kDataSourceShutdownRetryDelayMs);
+}
+
+base::FlatSet<DataSourceInstanceID> Unwinder::ConsumeAndUnwindReadySamples() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  base::FlatSet<DataSourceInstanceID> pending_sample_sources;
+
+  // Use a single snapshot of the ring buffer pointers.
+  ReadView read_view = unwind_queue_.BeginRead();
+
+  PERFETTO_METATRACE_COUNTER(
+      TAG_PRODUCER, PROFILER_UNWIND_QUEUE_SZ,
+      static_cast<int32_t>(read_view.write_pos - read_view.read_pos));
+
+  if (read_view.read_pos == read_view.write_pos)
+    return pending_sample_sources;
+
+  // Walk the queue.
+  for (auto read_pos = read_view.read_pos; read_pos < read_view.write_pos;
+       read_pos++) {
+    UnwindEntry& entry = unwind_queue_.at(read_pos);
+
+    if (!entry.valid)
+      continue;  // already processed
+
+    auto it = data_sources_.find(entry.data_source_id);
+    PERFETTO_CHECK(it != data_sources_.end());
+    DataSourceState& ds = it->second;
+
+    pid_t pid = entry.sample.pid;
+    ProcessState& proc_state = ds.process_states[pid];  // insert if new
+
+    // Giving up on the sample (proc-fd lookup timed out).
+    if (proc_state.status == ProcessState::Status::kExpired) {
+      PERFETTO_DLOG("Unwinder skipping sample for pid [%d]",
+                    static_cast<int>(pid));
+
+      delegate_->PostEmitUnwinderSkippedSample(entry.data_source_id,
+                                               std::move(entry.sample));
+      entry = UnwindEntry::Invalid();
+      continue;
+    }
+
+    // Still waiting on the proc-fds.
+    if (proc_state.status == ProcessState::Status::kResolving) {
+      PERFETTO_DLOG("Unwinder deferring sample for pid [%d]",
+                    static_cast<int>(pid));
+
+      pending_sample_sources.insert(entry.data_source_id);
+      continue;
+    }
+
+    // Sample ready - process it.
+    if (proc_state.status == ProcessState::Status::kResolved) {
+      // Metatrace: emit both a scoped slice, as well as a "counter"
+      // representing the pid being unwound.
+      PERFETTO_METATRACE_SCOPED(TAG_PRODUCER, PROFILER_UNWIND_SAMPLE);
+      PERFETTO_METATRACE_COUNTER(TAG_PRODUCER, PROFILER_UNWIND_CURRENT_PID,
+                                 static_cast<int32_t>(pid));
+
+      PERFETTO_CHECK(proc_state.unwind_state.has_value());
+      CompletedSample unwound_sample =
+          UnwindSample(entry.sample, &proc_state.unwind_state.value(),
+                       proc_state.attempted_unwinding);
+      proc_state.attempted_unwinding = true;
+
+      PERFETTO_METATRACE_COUNTER(TAG_PRODUCER, PROFILER_UNWIND_CURRENT_PID, 0);
+
+      delegate_->PostEmitSample(entry.data_source_id,
+                                std::move(unwound_sample));
+      entry = UnwindEntry::Invalid();
+      continue;
+    }
+  }
+
+  // Consume all leading processed entries in the queue.
+  auto new_read_pos = read_view.read_pos;
+  for (; new_read_pos < read_view.write_pos; new_read_pos++) {
+    UnwindEntry& entry = unwind_queue_.at(new_read_pos);
+    if (entry.valid)
+      break;
+  }
+  if (new_read_pos != read_view.read_pos)
+    unwind_queue_.CommitNewReadPosition(new_read_pos);
+
+  PERFETTO_METATRACE_COUNTER(
+      TAG_PRODUCER, PROFILER_UNWIND_QUEUE_SZ,
+      static_cast<int32_t>(read_view.write_pos - new_read_pos));
+
+  PERFETTO_DLOG("Unwind queue drain: [%" PRIu64 "]->[%" PRIu64 "]",
+                read_view.write_pos - read_view.read_pos,
+                read_view.write_pos - new_read_pos);
+
+  return pending_sample_sources;
+}
+
+CompletedSample Unwinder::UnwindSample(const ParsedSample& sample,
+                                       UnwindingMetadata* unwind_state,
+                                       bool pid_unwound_before) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  PERFETTO_DCHECK(unwind_state);
+
+  CompletedSample ret;
+  ret.cpu = sample.cpu;
+  ret.pid = sample.pid;
+  ret.tid = sample.tid;
+  ret.timestamp = sample.timestamp;
+  ret.cpu_mode = sample.cpu_mode;
+
+  // Overlay the stack bytes over /proc/<pid>/mem.
+  std::shared_ptr<unwindstack::Memory> overlay_memory =
+      std::make_shared<StackOverlayMemory>(
+          unwind_state->fd_mem, sample.regs->sp(),
+          reinterpret_cast<const uint8_t*>(sample.stack.data()),
+          sample.stack.size());
+
+  // Unwindstack clobbers registers, so make a copy in case we need to retry.
+  auto regs_copy = std::unique_ptr<unwindstack::Regs>{sample.regs->Clone()};
+
+  unwindstack::ErrorCode error_code = unwindstack::ERROR_NONE;
+  unwindstack::Unwinder unwinder(kUnwindingMaxFrames, &unwind_state->fd_maps,
+                                 regs_copy.get(), overlay_memory);
+
+  // TODO(rsavitski): consider rate-limiting unwind retries.
+  for (int attempt = 0; attempt < 2; attempt++) {
+    metatrace::ScopedEvent m(metatrace::TAG_PRODUCER,
+                             pid_unwound_before
+                                 ? metatrace::PROFILER_UNWIND_ATTEMPT
+                                 : metatrace::PROFILER_UNWIND_INITIAL_ATTEMPT);
+
+#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+    unwinder.SetJitDebug(unwind_state->jit_debug.get(), regs_copy->Arch());
+    unwinder.SetDexFiles(unwind_state->dex_files.get(), regs_copy->Arch());
+#endif
+    unwinder.Unwind(/*initial_map_names_to_skip=*/nullptr,
+                    /*map_suffixes_to_ignore=*/nullptr);
+    error_code = unwinder.LastErrorCode();
+    if (error_code != unwindstack::ERROR_INVALID_MAP)
+      break;
+
+    // Otherwise, reparse the maps, and possibly retry the unwind.
+    PERFETTO_DLOG("Reparsing maps for pid [%d]", static_cast<int>(sample.pid));
+    PERFETTO_METATRACE_SCOPED(TAG_PRODUCER, PROFILER_MAPS_REPARSE);
+    unwind_state->ReparseMaps();
+  }
+
+  PERFETTO_DLOG("Frames from unwindstack for pid [%d]:",
+                static_cast<int>(sample.pid));
+  std::vector<unwindstack::FrameData> frames = unwinder.ConsumeFrames();
+  ret.frames.reserve(frames.size());
+  for (unwindstack::FrameData& frame : frames) {
+    if (PERFETTO_DLOG_IS_ON())
+      PERFETTO_DLOG("%s", unwinder.FormatFrame(frame).c_str());
+
+    ret.frames.emplace_back(unwind_state->AnnotateFrame(std::move(frame)));
+  }
+
+  // In case of an unwinding error, add a synthetic error frame (which will
+  // appear as a caller of the partially-unwound fragment), for easier
+  // visualization of errors.
+  if (error_code != unwindstack::ERROR_NONE) {
+    PERFETTO_DLOG("Unwinding error %" PRIu8, error_code);
+    unwindstack::FrameData frame_data{};
+    frame_data.function_name =
+        "ERROR " + StringifyLibUnwindstackError(error_code);
+    frame_data.map_name = "ERROR";
+    ret.frames.emplace_back(std::move(frame_data), /*build_id=*/"");
+    ret.unwind_error = error_code;
+  }
+
+  return ret;
+}
+
+void Unwinder::PostInitiateDataSourceStop(DataSourceInstanceID ds_id) {
+  task_runner_->PostTask([this, ds_id] { InitiateDataSourceStop(ds_id); });
+}
+
+void Unwinder::InitiateDataSourceStop(DataSourceInstanceID ds_id) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  PERFETTO_DLOG("Unwinder::InitiateDataSourceStop(%zu)",
+                static_cast<size_t>(ds_id));
+
+  auto it = data_sources_.find(ds_id);
+  PERFETTO_CHECK(it != data_sources_.end());
+  DataSourceState& ds = it->second;
+
+  PERFETTO_CHECK(ds.status == DataSourceState::Status::kActive);
+  ds.status = DataSourceState::Status::kShuttingDown;
+
+  // Make sure that there's an outstanding task to process the unwinding queue,
+  // as it is the point that evaluates the stop condition.
+  PostProcessQueue();
+}
+
+void Unwinder::FinishDataSourceStop(DataSourceInstanceID ds_id) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  PERFETTO_DLOG("Unwinder::FinishDataSourceStop(%zu)",
+                static_cast<size_t>(ds_id));
+
+  auto it = data_sources_.find(ds_id);
+  PERFETTO_CHECK(it != data_sources_.end());
+  DataSourceState& ds = it->second;
+
+  // Drop unwinder's state tied to the source.
+  PERFETTO_CHECK(ds.status == DataSourceState::Status::kShuttingDown);
+  data_sources_.erase(it);
+
+  // Clean up state if there are no more active sources.
+  if (data_sources_.empty())
+    ResetAndEnableUnwindstackCache();
+
+  // Inform service thread that the unwinder is done with the source.
+  delegate_->PostFinishDataSourceStop(ds_id);
+}
+
+void Unwinder::PostClearCachedStatePeriodic(DataSourceInstanceID ds_id,
+                                            uint32_t period_ms) {
+  task_runner_->PostDelayedTask(
+      [this, ds_id, period_ms] { ClearCachedStatePeriodic(ds_id, period_ms); },
+      period_ms);
+}
+
+// See header for rationale.
+void Unwinder::ClearCachedStatePeriodic(DataSourceInstanceID ds_id,
+                                        uint32_t period_ms) {
+  auto it = data_sources_.find(ds_id);
+  if (it == data_sources_.end())
+    return;  // stop the periodic task
+
+  DataSourceState& ds = it->second;
+  if (ds.status != DataSourceState::Status::kActive)
+    return;
+
+  PERFETTO_METATRACE_SCOPED(TAG_PRODUCER, PROFILER_UNWIND_CACHE_CLEAR);
+  PERFETTO_DLOG("Clearing unwinder's cached state.");
+
+  for (auto& pid_and_process : ds.process_states) {
+    pid_and_process.second.unwind_state->fd_maps.Reset();
+  }
+  ResetAndEnableUnwindstackCache();
+  MaybeReleaseAllocatorMemToOS();
+
+  PostClearCachedStatePeriodic(ds_id, period_ms);  // repost
+}
+
+void Unwinder::ResetAndEnableUnwindstackCache() {
+  PERFETTO_DLOG("Resetting unwindstack cache");
+  // Libunwindstack uses an unsynchronized variable for setting/checking whether
+  // the cache is enabled. Therefore unwinding and cache toggling should stay on
+  // the same thread, but we might be moving unwinding across threads if we're
+  // recreating |Unwinder| instances (during a reconnect to traced). Therefore,
+  // use our own static lock to synchronize the cache toggling.
+  // TODO(rsavitski): consider fixing this in libunwindstack itself.
+  static std::mutex* lock = new std::mutex{};
+  std::lock_guard<std::mutex> guard{*lock};
+  unwindstack::Elf::SetCachingEnabled(false);  // free any existing state
+  unwindstack::Elf::SetCachingEnabled(true);   // reallocate a fresh cache
+}
+
+}  // namespace profiling
+}  // namespace perfetto
diff --git a/src/profiling/perf/unwinding.h b/src/profiling/perf/unwinding.h
new file mode 100644
index 0000000..46c3760
--- /dev/null
+++ b/src/profiling/perf/unwinding.h
@@ -0,0 +1,265 @@
+/*
+ * 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_PROFILING_PERF_UNWINDING_H_
+#define SRC_PROFILING_PERF_UNWINDING_H_
+
+#include <map>
+#include <thread>
+
+#include <linux/perf_event.h>
+#include <stdint.h>
+
+#include <unwindstack/Error.h>
+
+#include "perfetto/base/flat_set.h"
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/thread_checker.h"
+#include "perfetto/ext/base/unix_task_runner.h"
+#include "perfetto/ext/tracing/core/basic_types.h"
+#include "src/profiling/common/unwind_support.h"
+#include "src/profiling/perf/common_types.h"
+#include "src/profiling/perf/unwind_queue.h"
+
+namespace perfetto {
+namespace profiling {
+
+constexpr static uint32_t kUnwindQueueCapacity = 2048;
+
+// Unwinds callstacks based on the sampled stack and register state (see
+// |ParsedSample|). Has a single unwinding ring queue, shared across
+// all data sources.
+//
+// Samples cannot be unwound without having /proc/<pid>/{maps,mem} file
+// descriptors for that process. This lookup can be asynchronous (e.g. on
+// Android), so the unwinder might have to wait before it can process (or
+// discard) some of the enqueued samples. To avoid blocking the entire queue,
+// the unwinder is allowed to process the entries out of order.
+//
+// Besides the queue, all interactions between the unwinder and the rest of the
+// producer logic are through posted tasks.
+//
+// As unwinding times are long-tailed (example measurements: median <1ms,
+// worst-case ~1000ms), the unwinder runs on a dedicated thread to avoid
+// starving the rest of the producer's work (including IPC and consumption of
+// records from the kernel ring buffers).
+//
+// This class should not be instantiated directly, use the |UnwinderHandle|
+// below instead.
+//
+// TODO(rsavitski): while the inputs to the unwinder are batched as a result of
+// the reader posting a wakeup only after consuming a batch of kernel samples,
+// the Unwinder might be staggering wakeups for the producer thread by posting a
+// task every time a sample has been unwound. Evaluate how bad these wakeups are
+// in practice, and consider also implementing a batching strategy for the
+// unwinder->serialization handoff (which isn't very latency-sensitive).
+class Unwinder {
+ public:
+  friend class UnwinderHandle;
+
+  // Callbacks from the unwinder to the primary producer thread.
+  class Delegate {
+   public:
+    virtual void PostEmitSample(DataSourceInstanceID ds_id,
+                                CompletedSample sample) = 0;
+    virtual void PostEmitUnwinderSkippedSample(DataSourceInstanceID ds_id,
+                                               ParsedSample sample) = 0;
+    virtual void PostFinishDataSourceStop(DataSourceInstanceID ds_id) = 0;
+
+    virtual ~Delegate();
+  };
+
+  ~Unwinder() { PERFETTO_DCHECK_THREAD(thread_checker_); }
+
+  void PostStartDataSource(DataSourceInstanceID ds_id);
+  void PostAdoptProcDescriptors(DataSourceInstanceID ds_id,
+                                pid_t pid,
+                                base::ScopedFile maps_fd,
+                                base::ScopedFile mem_fd);
+  void PostRecordTimedOutProcDescriptors(DataSourceInstanceID ds_id, pid_t pid);
+  void PostProcessQueue();
+  void PostInitiateDataSourceStop(DataSourceInstanceID ds_id);
+
+  void PostClearCachedStatePeriodic(DataSourceInstanceID ds_id,
+                                    uint32_t period_ms);
+
+  UnwindQueue<UnwindEntry, kUnwindQueueCapacity>& unwind_queue() {
+    return unwind_queue_;
+  }
+
+ private:
+  struct ProcessState {
+    enum class Status {
+      kResolving,  // unwinder waiting on proc-fds for the process
+      kResolved,   // proc-fds available, can unwind samples
+      kExpired     // proc-fd lookup timed out, will discard samples
+    };
+
+    Status status = Status::kResolving;
+    // Present iff status == kResolved.
+    base::Optional<UnwindingMetadata> unwind_state;
+    // Used to distinguish first-time unwinding attempts for a process, for
+    // logging purposes.
+    bool attempted_unwinding = false;
+  };
+
+  struct DataSourceState {
+    enum class Status { kActive, kShuttingDown };
+
+    Status status = Status::kActive;
+    std::map<pid_t, ProcessState> process_states;
+  };
+
+  // Must be instantiated via the |UnwinderHandle|.
+  Unwinder(Delegate* delegate, base::UnixTaskRunner* task_runner);
+
+  // Marks the data source as valid and active at the unwinding stage.
+  void StartDataSource(DataSourceInstanceID ds_id);
+
+  void AdoptProcDescriptors(DataSourceInstanceID ds_id,
+                            pid_t pid,
+                            base::ScopedFile maps_fd,
+                            base::ScopedFile mem_fd);
+  void RecordTimedOutProcDescriptors(DataSourceInstanceID ds_id, pid_t pid);
+
+  // Primary task. Processes the enqueued samples using
+  // |ConsumeAndUnwindReadySamples|, and re-evaluates data source state.
+  void ProcessQueue();
+
+  // Processes the enqueued samples for which all unwinding inputs are ready.
+  // Returns the set of data source instances which still have samples pending
+  // (i.e. waiting on the proc-fds).
+  base::FlatSet<DataSourceInstanceID> ConsumeAndUnwindReadySamples();
+
+  CompletedSample UnwindSample(const ParsedSample& sample,
+                               UnwindingMetadata* unwind_state,
+                               bool pid_unwound_before);
+
+  // Marks the data source as shutting down at the unwinding stage. It is known
+  // that no new samples for this source will be pushed into the queue, but we
+  // need to delay the unwinder state teardown until all previously-enqueued
+  // samples for this source are processed.
+  void InitiateDataSourceStop(DataSourceInstanceID ds_id);
+
+  // Tears down unwinding state for the data source without any outstanding
+  // samples, and informs the service that it can continue the shutdown
+  // sequence.
+  void FinishDataSourceStop(DataSourceInstanceID ds_id);
+
+  // Clears the parsed maps for all previously-sampled processes, and resets the
+  // libunwindstack cache. This has the effect of deallocating the cached Elf
+  // objects within libunwindstack, which take up non-trivial amounts of memory.
+  //
+  // There are two reasons for having this operation:
+  // * over a longer trace, it's desireable to drop heavy state for processes
+  //   that haven't been sampled recently.
+  // * since libunwindstack's cache is not bounded, it'll tend towards having
+  //   state for all processes that are targeted by the profiling config.
+  //   Clearing the cache periodically helps keep its footprint closer to the
+  //   actual working set (NB: which might still be arbitrarily big, depending
+  //   on the profiling config).
+  //
+  // After this function completes, the next unwind for each process will
+  // therefore incur a guaranteed maps reparse.
+  //
+  // Unwinding for concurrent data sources will *not* be directly affected at
+  // the time of writing, as the non-cleared parsed maps will keep the cached
+  // Elf objects alive through shared_ptrs.
+  //
+  // Note that this operation is heavy in terms of cpu%, and should therefore
+  // be called only for profiling configs that require it.
+  //
+  // TODO(rsavitski): dropping the full parsed maps is somewhat excessive, could
+  // instead clear just the |MapInfo.elf| shared_ptr, but that's considered too
+  // brittle as it's an implementation detail of libunwindstack.
+  // TODO(rsavitski): improve libunwindstack cache's architecture (it is still
+  // worth having at the moment to speed up unwinds across map reparses).
+  void ClearCachedStatePeriodic(DataSourceInstanceID ds_id, uint32_t period_ms);
+
+  void ResetAndEnableUnwindstackCache();
+
+  base::UnixTaskRunner* const task_runner_;
+  Delegate* const delegate_;
+  UnwindQueue<UnwindEntry, kUnwindQueueCapacity> unwind_queue_;
+  std::map<DataSourceInstanceID, DataSourceState> data_sources_;
+
+  PERFETTO_THREAD_CHECKER(thread_checker_)
+};
+
+// Owning resource handle for an |Unwinder| with a dedicated task thread.
+// Ensures that the |Unwinder| is constructed and destructed on the task thread.
+// TODO(rsavitski): update base::ThreadTaskRunner to allow for this pattern of
+// owned state, and consolidate.
+class UnwinderHandle {
+ public:
+  UnwinderHandle(Unwinder::Delegate* delegate) {
+    std::mutex init_lock;
+    std::condition_variable init_cv;
+
+    std::function<void(base::UnixTaskRunner*, Unwinder*)> initializer =
+        [this, &init_lock, &init_cv](base::UnixTaskRunner* task_runner,
+                                     Unwinder* unwinder) {
+          std::lock_guard<std::mutex> lock(init_lock);
+          task_runner_ = task_runner;
+          unwinder_ = unwinder;
+          // Notify while still holding the lock, as init_cv ceases to exist as
+          // soon as the main thread observes a non-null task_runner_, and it
+          // can wake up spuriously (i.e. before the notify if we had unlocked
+          // before notifying).
+          init_cv.notify_one();
+        };
+
+    thread_ = std::thread(&UnwinderHandle::RunTaskThread, this,
+                          std::move(initializer), delegate);
+
+    std::unique_lock<std::mutex> lock(init_lock);
+    init_cv.wait(lock, [this] { return !!task_runner_ && !!unwinder_; });
+  }
+
+  ~UnwinderHandle() {
+    if (task_runner_) {
+      PERFETTO_CHECK(!task_runner_->QuitCalled());
+      task_runner_->Quit();
+
+      PERFETTO_DCHECK(thread_.joinable());
+    }
+    if (thread_.joinable())
+      thread_.join();
+  }
+
+  Unwinder* operator->() { return unwinder_; }
+
+ private:
+  void RunTaskThread(
+      std::function<void(base::UnixTaskRunner*, Unwinder*)> initializer,
+      Unwinder::Delegate* delegate) {
+    base::UnixTaskRunner task_runner;
+    Unwinder unwinder(delegate, &task_runner);
+    task_runner.PostTask(
+        std::bind(std::move(initializer), &task_runner, &unwinder));
+    task_runner.Run();
+  }
+
+  std::thread thread_;
+  base::UnixTaskRunner* task_runner_ = nullptr;
+  Unwinder* unwinder_ = nullptr;
+};
+
+}  // namespace profiling
+}  // namespace perfetto
+
+#endif  // SRC_PROFILING_PERF_UNWINDING_H_
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/profiling/symbolizer/local_symbolizer.cc b/src/profiling/symbolizer/local_symbolizer.cc
index 1874df1..d0290da 100644
--- a/src/profiling/symbolizer/local_symbolizer.cc
+++ b/src/profiling/symbolizer/local_symbolizer.cc
@@ -326,7 +326,8 @@
     PERFETTO_CHECK(dup2(*output_pipe_.wr, STDOUT_FILENO) != -1);
     input_pipe_.wr.reset();
     output_pipe_.rd.reset();
-    PERFETTO_CHECK(execvp(file.c_str(), &(c_str_args[0])) != -1);
+    if (execvp(file.c_str(), &(c_str_args[0])) == -1)
+      PERFETTO_FATAL("Failed to exec %s", file.c_str());
   }
   PERFETTO_CHECK(pid_ != -1);
   input_pipe_.rd.reset();
@@ -337,7 +338,7 @@
   if (pid_ != -1) {
     kill(pid_, SIGKILL);
     int wstatus;
-    waitpid(pid_, &wstatus, 0);
+    PERFETTO_EINTR(waitpid(pid_, &wstatus, 0));
   }
 }
 
diff --git a/src/profiling/symbolizer/symbolize_database.cc b/src/profiling/symbolizer/symbolize_database.cc
index 23ab640..462e836 100644
--- a/src/profiling/symbolizer/symbolize_database.cc
+++ b/src/profiling/symbolizer/symbolize_database.cc
@@ -41,7 +41,7 @@
     "from stack_profile_frame spf "
     "join stack_profile_mapping spm "
     "on spf.mapping = spm.id "
-    "where spm.build_id != '' and spf.symbol_set_id == 0";
+    "where spm.build_id != '' and spf.symbol_set_id IS NULL";
 
 using NameAndBuildIdPair = std::pair<std::string, std::string>;
 
@@ -77,8 +77,8 @@
   Iterator it = tp->ExecuteQuery(kQueryUnsymbolized);
   while (it.Next()) {
     auto name_and_buildid =
-        std::make_pair(it.Get(0).string_value, FromHex(it.Get(1).string_value));
-    int64_t rel_pc = it.Get(2).long_value;
+        std::make_pair(it.Get(0).AsString(), FromHex(it.Get(1).AsString()));
+    int64_t rel_pc = it.Get(2).AsLong();
     res[name_and_buildid].emplace_back(rel_pc);
   }
   if (!it.Status().ok()) {
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/field.cc b/src/protozero/field.cc
index c92f7ce..be16482 100644
--- a/src/protozero/field.cc
+++ b/src/protozero/field.cc
@@ -26,7 +26,8 @@
 
 namespace protozero {
 
-void Field::SerializeAndAppendTo(std::string* dst) {
+template <typename Container>
+void Field::SerializeAndAppendToInternal(Container* dst) const {
   namespace pu = proto_utils;
   size_t initial_size = dst->size();
   dst->resize(initial_size + pu::kMaxSimpleFieldEncodedSize + size_);
@@ -68,4 +69,12 @@
   dst->resize(initial_size + written_size);
 }
 
+void Field::SerializeAndAppendTo(std::string* dst) const {
+  SerializeAndAppendToInternal(dst);
+}
+
+void Field::SerializeAndAppendTo(std::vector<uint8_t>* dst) const {
+  SerializeAndAppendToInternal(dst);
+}
+
 }  // namespace protozero
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/protoc_plugin/cppgen_plugin.cc b/src/protozero/protoc_plugin/cppgen_plugin.cc
index cc499ab..514393b 100644
--- a/src/protozero/protoc_plugin/cppgen_plugin.cc
+++ b/src/protozero/protoc_plugin/cppgen_plugin.cc
@@ -490,7 +490,6 @@
   // enum MyEnum { FOO=1, BAR=2 }
   // Hence this |prefix| logic.
   std::string prefix = enum_desc->containing_type() ? full_name + "_" : "";
-
   p->Print("enum $f$ : int {\n", "f", full_name);
   for (int e = 0; e < enum_desc->value_count(); e++) {
     const EnumValueDescriptor* value = enum_desc->value(e);
@@ -502,12 +501,28 @@
 
 void CppObjGenerator::GenEnumAliases(const EnumDescriptor* enum_desc,
                                      Printer* p) const {
+  int min_value = std::numeric_limits<int>::max();
+  int max_value = std::numeric_limits<int>::min();
+  std::string min_name;
+  std::string max_name;
   std::string full_name = GetFullName(enum_desc);
   for (int e = 0; e < enum_desc->value_count(); e++) {
     const EnumValueDescriptor* value = enum_desc->value(e);
     p->Print("static constexpr auto $n$ = $f$_$n$;\n", "f", full_name, "n",
              value->name());
+    if (value->number() < min_value) {
+      min_value = value->number();
+      min_name = full_name + "_" + value->name();
+    }
+    if (value->number() > max_value) {
+      max_value = value->number();
+      max_name = full_name + "_" + value->name();
+    }
   }
+  p->Print("static constexpr auto $n$_MIN = $m$;\n", "n", enum_desc->name(),
+           "m", min_name);
+  p->Print("static constexpr auto $n$_MAX = $m$;\n", "n", enum_desc->name(),
+           "m", max_name);
 }
 
 void CppObjGenerator::GenClassDecl(const Descriptor* msg, Printer* p) const {
diff --git a/src/protozero/test/example_proto/test_messages.descriptor.h b/src/protozero/test/example_proto/test_messages.descriptor.h
index f27e4ec..cdda498 100644
--- a/src/protozero/test/example_proto/test_messages.descriptor.h
+++ b/src/protozero/test/example_proto/test_messages.descriptor.h
@@ -25,7 +25,7 @@
 // This file was autogenerated by tools/gen_binary_descriptors. Do not edit.
 
 // SHA1(tools/gen_binary_descriptors)
-// d6628b15181dba5287e35b56b966b39ea93d42b1
+// 3df80477da2ea38cc659967487b37051a154bb69
 // SHA1(src/protozero/test/example_proto/test_messages.proto)
 // bd59c7719b091c8277d2dce867d28dfa5b40bab2
 
@@ -223,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, 0x4d, 0x6f,
+     0x6f, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6d, 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, 0x55, 0x52, 0x4c, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72,
+     0x52, 0x0a, 0x75, 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, 0x58, 0x4d, 0x61, 0x70, 0x12, 0x21, 0x0a, 0x0d,
+     0x28, 0x08, 0x52, 0x04, 0x78, 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, 0x55, 0x72, 0x4c,
+     0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x75, 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, 0x42, 0x69, 0x67, 0x42, 0x61, 0x6e, 0x67, 0x12,
+     0x28, 0x08, 0x52, 0x07, 0x62, 0x69, 0x67, 0x42, 0x61, 0x6e, 0x67, 0x12,
      0x0e, 0x0a, 0x02, 0x55, 0x32, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52,
-     0x02, 0x55, 0x32, 0x12, 0x1a, 0x0a, 0x09, 0x62, 0x61, 0x6e, 0x67, 0x42,
+     0x02, 0x75, 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,
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 97e7ed0..93b7969 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -26,9 +26,7 @@
 if (enable_perfetto_trace_processor_sqlite) {
   static_library("trace_processor") {
     complete_static_lib = true
-    deps = [
-      ":lib",
-    ]
+    deps = [ ":lib" ]
   }
 }
 
@@ -44,62 +42,53 @@
   }
 }
 
-source_set("protozero_to_text") {
-  sources = [
-    "importers/proto/track_event.descriptor.h",
-    "protozero_to_text.cc",
-    "protozero_to_text.h",
-  ]
-  deps = [
-    ":descriptors",
-    "../../gn:default_deps",
-    "../../protos/perfetto/common:zero",
-    "../../protos/perfetto/trace/track_event:zero",
-    "../base",
-    "../protozero:protozero",
-  ]
+source_set("track_event_descriptor") {
+  sources = [ "importers/proto/track_event.descriptor.h" ]
 }
 
-source_set("descriptors") {
+source_set("ftrace_descriptors") {
   sources = [
-    "descriptors.cc",
-    "descriptors.h",
+    "importers/ftrace/ftrace_descriptors.cc",
+    "importers/ftrace/ftrace_descriptors.h",
   ]
   deps = [
     "../../gn:default_deps",
-    "../../include/perfetto/trace_processor",
-    "../../protos/perfetto/common:zero",
-    "../base",
-    "../protozero:protozero",
+    "../../include/perfetto/ext/base:base",
+    "../protozero",
   ]
 }
 
 source_set("storage_minimal") {
   sources = [
-    "args_tracker.cc",
-    "args_tracker.h",
     "chunked_trace_reader.h",
-    "clock_tracker.cc",
-    "clock_tracker.h",
-    "destructible.cc",
-    "destructible.h",
-    "event_tracker.cc",
-    "event_tracker.h",
     "forwarding_trace_parser.cc",
     "forwarding_trace_parser.h",
-    "ftrace_utils.h",
-    "global_args_tracker.cc",
-    "global_args_tracker.h",
-    "heap_profile_tracker.cc",
-    "heap_profile_tracker.h",
+    "importers/default_modules.cc",
+    "importers/default_modules.h",
     "importers/ftrace/ftrace_module.cc",
     "importers/ftrace/ftrace_module.h",
     "importers/fuchsia/fuchsia_record.h",
+    "importers/fuchsia/fuchsia_trace_utils.h",
+    "importers/gzip/gzip_utils.cc",
+    "importers/gzip/gzip_utils.h",
+    "importers/json/json_utils.cc",
+    "importers/json/json_utils.h",
+    "importers/ninja/ninja_log_parser.cc",
+    "importers/ninja/ninja_log_parser.h",
     "importers/proto/args_table_utils.cc",
     "importers/proto/args_table_utils.h",
-    "importers/proto/chrome_compositor_scheduler_state.descriptor.h",
+    "importers/proto/heap_profile_tracker.cc",
+    "importers/proto/heap_profile_tracker.h",
+    "importers/proto/metadata_tracker.cc",
+    "importers/proto/metadata_tracker.h",
     "importers/proto/packet_sequence_state.cc",
     "importers/proto/packet_sequence_state.h",
+    "importers/proto/perf_sample_tracker.cc",
+    "importers/proto/perf_sample_tracker.h",
+    "importers/proto/profile_module.cc",
+    "importers/proto/profile_module.h",
+    "importers/proto/profile_packet_utils.cc",
+    "importers/proto/profile_packet_utils.h",
     "importers/proto/proto_importer_module.cc",
     "importers/proto/proto_importer_module.h",
     "importers/proto/proto_incremental_state.h",
@@ -107,47 +96,39 @@
     "importers/proto/proto_trace_parser.h",
     "importers/proto/proto_trace_tokenizer.cc",
     "importers/proto/proto_trace_tokenizer.h",
+    "importers/proto/stack_profile_tracker.cc",
+    "importers/proto/stack_profile_tracker.h",
     "importers/proto/track_event_module.cc",
     "importers/proto/track_event_module.h",
     "importers/proto/track_event_parser.cc",
     "importers/proto/track_event_parser.h",
     "importers/proto/track_event_tokenizer.cc",
     "importers/proto/track_event_tokenizer.h",
-    "metadata.h",
-    "metadata_tracker.cc",
-    "metadata_tracker.h",
-    "process_tracker.cc",
-    "process_tracker.h",
-    "slice_tracker.cc",
-    "slice_tracker.h",
-    "stack_profile_tracker.cc",
-    "stack_profile_tracker.h",
-    "stats.h",
-    "syscall_tracker.h",
+    "importers/syscalls/syscall_tracker.h",
+    "importers/systrace/systrace_line.h",
     "timestamped_trace_piece.h",
     "trace_blob_view.h",
     "trace_parser.h",
     "trace_processor_context.cc",
-    "trace_processor_context.h",
     "trace_processor_storage.cc",
     "trace_processor_storage_impl.cc",
     "trace_processor_storage_impl.h",
     "trace_sorter.cc",
     "trace_sorter.h",
-    "trace_storage.cc",
-    "trace_storage.h",
-    "track_tracker.cc",
-    "track_tracker.h",
     "virtual_destructors.cc",
   ]
   deps = [
-    ":descriptors",
+    ":track_event_descriptor",
     "../../gn:default_deps",
     "../base",
     "../protozero",
     "containers",
+    "importers:common",
+    "storage",
     "tables",
     "types",
+    "util",
+    "util:descriptors",
   ]
   public_deps = [
     "../../include/perfetto/trace_processor:storage",
@@ -163,46 +144,27 @@
     "../../protos/perfetto/trace/profiling:zero",
     "../../protos/perfetto/trace/ps:zero",
     "../../protos/perfetto/trace/sys_stats:zero",
+    "../../protos/perfetto/trace/system_info: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",
-      "importers/json/json_trace_parser.h",
-      "importers/json/json_trace_tokenizer.cc",
-      "importers/json/json_trace_tokenizer.h",
-      "importers/json/json_trace_utils.cc",
-      "importers/json/json_trace_utils.h",
-    ]
+
+  # json_utils optionally depends on jsoncpp.
+  if (enable_perfetto_trace_processor_json) {
     deps += [ "../../gn:jsoncpp" ]
   }
-  if (enable_perfetto_trace_processor_fuchsia) {
-    sources += [
-      "ftrace_utils.cc",
-      "importers/fuchsia/fuchsia_record.cc",
-      "importers/fuchsia/fuchsia_trace_parser.cc",
-      "importers/fuchsia/fuchsia_trace_parser.h",
-      "importers/fuchsia/fuchsia_trace_tokenizer.cc",
-      "importers/fuchsia/fuchsia_trace_tokenizer.h",
-      "importers/fuchsia/fuchsia_trace_utils.cc",
-      "importers/fuchsia/fuchsia_trace_utils.h",
-    ]
+
+  # gzip_utils optionally depends on zlib.
+  if (enable_perfetto_zlib) {
+    deps += [ "../../gn:zlib" ]
   }
 }
 
 source_set("storage_full") {
   sources = [
+    "importers/additional_modules.cc",
+    "importers/additional_modules.h",
     "importers/ftrace/binder_tracker.cc",
     "importers/ftrace/binder_tracker.h",
-    "importers/ftrace/ftrace_descriptors.cc",
-    "importers/ftrace/ftrace_descriptors.h",
     "importers/ftrace/ftrace_module_impl.cc",
     "importers/ftrace/ftrace_module_impl.h",
     "importers/ftrace/ftrace_parser.cc",
@@ -213,10 +175,26 @@
     "importers/ftrace/rss_stat_tracker.h",
     "importers/ftrace/sched_event_tracker.cc",
     "importers/ftrace/sched_event_tracker.h",
+    "importers/fuchsia/fuchsia_record.cc",
+    "importers/fuchsia/fuchsia_trace_parser.cc",
+    "importers/fuchsia/fuchsia_trace_parser.h",
+    "importers/fuchsia/fuchsia_trace_tokenizer.cc",
+    "importers/fuchsia/fuchsia_trace_tokenizer.h",
+    "importers/fuchsia/fuchsia_trace_utils.cc",
+    "importers/gzip/gzip_trace_parser.cc",
+    "importers/gzip/gzip_trace_parser.h",
+    "importers/json/json_trace_parser.cc",
+    "importers/json/json_trace_parser.h",
+    "importers/json/json_trace_tokenizer.cc",
+    "importers/json/json_trace_tokenizer.h",
+    "importers/json/json_tracker.cc",
+    "importers/json/json_tracker.h",
     "importers/proto/android_probes_module.cc",
     "importers/proto/android_probes_module.h",
     "importers/proto/android_probes_parser.cc",
     "importers/proto/android_probes_parser.h",
+    "importers/proto/android_probes_tracker.cc",
+    "importers/proto/android_probes_tracker.h",
     "importers/proto/graphics_event_module.cc",
     "importers/proto/graphics_event_module.h",
     "importers/proto/graphics_event_parser.cc",
@@ -233,22 +211,24 @@
     "importers/proto/system_probes_parser.h",
     "importers/proto/vulkan_memory_tracker.cc",
     "importers/proto/vulkan_memory_tracker.h",
+    "importers/syscalls/syscall_tracker.cc",
+    "importers/syscalls/syscalls_aarch32.h",
+    "importers/syscalls/syscalls_aarch64.h",
+    "importers/syscalls/syscalls_armeabi.h",
+    "importers/syscalls/syscalls_x86.h",
+    "importers/syscalls/syscalls_x86_64.h",
+    "importers/systrace/systrace_line_parser.cc",
+    "importers/systrace/systrace_line_parser.h",
+    "importers/systrace/systrace_line_tokenizer.cc",
+    "importers/systrace/systrace_line_tokenizer.h",
     "importers/systrace/systrace_parser.cc",
     "importers/systrace/systrace_parser.h",
     "importers/systrace/systrace_trace_parser.cc",
     "importers/systrace/systrace_trace_parser.h",
-    "register_additional_modules.cc",
-    "register_additional_modules.h",
-    "syscall_tracker.cc",
-    "syscalls_aarch32.h",
-    "syscalls_aarch64.h",
-    "syscalls_armeabi.h",
-    "syscalls_x86_64.h",
   ]
-  public_deps = [
-    ":storage_minimal",
-  ]
+  public_deps = [ ":storage_minimal" ]
   deps = [
+    ":ftrace_descriptors",
     "../../include/perfetto/ext/base:base",
     "../../include/perfetto/ext/traced:sys_stats_counters",
     "../../protos/perfetto/common:zero",
@@ -257,71 +237,52 @@
     "../../protos/perfetto/trace/gpu:zero",
     "../../protos/perfetto/trace/interned_data:zero",
     "../protozero",
+    "importers:common",
+    "storage",
     "types",
   ]
   if (enable_perfetto_trace_processor_json) {
-    public_deps += [ "../../gn:jsoncpp" ]
-  }
-
-  # Include these sources only if they are not already included in
-  # storage_minimal.
-  if (!enable_perfetto_trace_processor_fuchsia) {
-    sources += [ "ftrace_utils.cc" ]
+    deps += [ "../../gn:jsoncpp" ]
   }
 }
 
-if (enable_perfetto_trace_processor_json) {
-  source_set("export_json") {
-    sources = [
-      "export_json.cc",
-      "export_json.h",
-    ]
-    deps = [
-      ":storage_minimal",
-      "../../gn:default_deps",
-      "../../gn:jsoncpp",
-      "../base",
-    ]
-    public_deps = [
-      "../../include/perfetto/ext/trace_processor:export_json",
-    ]
+source_set("export_json") {
+  sources = [
+    "export_json.cc",
+    "export_json.h",
+  ]
+  deps = [
+    ":storage_minimal",
+    "../../gn:default_deps",
+    "../base",
+    "storage",
+    "types",
+  ]
+  public_deps = [ "../../include/perfetto/ext/trace_processor:export_json" ]
+  if (enable_perfetto_trace_processor_json) {
+    deps += [ "../../gn:jsoncpp" ]
   }
 }
 
 if (enable_perfetto_trace_processor_sqlite) {
   source_set("lib") {
     sources = [
-      "filtered_row_index.cc",
-      "filtered_row_index.h",
+      "dynamic/describe_slice_generator.cc",
+      "dynamic/describe_slice_generator.h",
+      "dynamic/experimental_counter_dur_generator.cc",
+      "dynamic/experimental_counter_dur_generator.h",
+      "dynamic/experimental_flamegraph_generator.cc",
+      "dynamic/experimental_flamegraph_generator.h",
+      "dynamic/experimental_slice_layout_generator.cc",
+      "dynamic/experimental_slice_layout_generator.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 = [
+      ":export_json",
       ":storage_full",
       "../../gn:default_deps",
       "../../gn:sqlite",
@@ -329,17 +290,18 @@
       "../../protos/perfetto/metrics/android:zero",
       "../../protos/perfetto/trace/ftrace:zero",
       "../base",
+      "analysis",
       "db:lib",
+      "importers:common",
       "metrics:lib",
       "sqlite",
+      "storage",
       "tables",
       "types",
     ]
-    public_deps = [
-      "../../include/perfetto/trace_processor",
-    ]
+    public_deps = [ "../../include/perfetto/trace_processor" ]
     if (enable_perfetto_trace_processor_json) {
-      deps += [ ":export_json" ]
+      deps += [ "../../gn:jsoncpp" ]
     }
   }
 
@@ -352,6 +314,7 @@
       "../../src/profiling/symbolizer:symbolize_database",
       "../base",
       "metrics:lib",
+      "util",
     ]
     if (enable_perfetto_version_gen) {
       deps += [ "../../gn/standalone:gen_git_revision" ]
@@ -363,9 +326,9 @@
       deps += [ "rpc:httpd" ]
     }
     sources = [
-      "proto_to_json.cc",
-      "proto_to_json.h",
       "trace_processor_shell.cc",
+      "util/proto_to_json.cc",
+      "util/proto_to_json.h",
     ]
   }
 }  # if (enable_perfetto_trace_processor_sqlite)
@@ -373,26 +336,19 @@
 perfetto_unittest_source_set("unittests") {
   testonly = true
   sources = [
-    "clock_tracker_unittest.cc",
-    "event_tracker_unittest.cc",
     "forwarding_trace_parser_unittest.cc",
-    "ftrace_utils_unittest.cc",
-    "heap_profile_tracker_unittest.cc",
+    "importers/ftrace/sched_event_tracker_unittest.cc",
+    "importers/fuchsia/fuchsia_trace_utils_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/heap_profile_tracker_unittest.cc",
     "importers/proto/proto_trace_parser_unittest.cc",
+    "importers/syscalls/syscall_tracker_unittest.cc",
     "importers/systrace/systrace_parser_unittest.cc",
-    "process_tracker_unittest.cc",
-    "protozero_to_text_unittests.cc",
-    "sched_slice_table_unittest.cc",
-    "slice_tracker_unittest.cc",
-    "syscall_tracker_unittest.cc",
     "trace_sorter_unittest.cc",
   ]
   deps = [
-    ":descriptors",
-    ":protozero_to_text",
     ":storage_full",
     "../../gn:default_deps",
     "../../gn:gtest_and_gmock",
@@ -404,7 +360,6 @@
     "../../protos/perfetto/trace/ftrace:zero",
     "../../protos/perfetto/trace/gpu:zero",
     "../../protos/perfetto/trace/interned_data:zero",
-    "../../protos/perfetto/trace/profiling:zero",
     "../../protos/perfetto/trace/ps:zero",
     "../../protos/perfetto/trace/sys_stats:zero",
     "../../protos/perfetto/trace/track_event:zero",
@@ -413,44 +368,45 @@
     "../protozero:testing_messages_zero",
     "containers:unittests",
     "db:unittests",
+    "importers:common",
+    "importers:unittests",
+    "storage",
     "tables:unittests",
+    "types",
+    "types:unittests",
+    "util:unittests",
   ]
 
   if (enable_perfetto_trace_processor_sqlite) {
     sources += [
-      "filtered_row_index_unittest.cc",
-      "span_join_operator_table_unittest.cc",
+      "dynamic/experimental_counter_dur_generator_unittest.cc",
+      "dynamic/experimental_slice_layout_generator_unittest.cc",
     ]
     deps += [
       ":lib",
       "../../gn:sqlite",
-      "sqlite",
       "sqlite:unittests",
     ]
   }
 
   if (enable_perfetto_trace_processor_json) {
-    if (enable_perfetto_trace_processor_json_import) {
-      sources += [
-        "importers/json/json_trace_tokenizer_unittest.cc",
-        "importers/json/json_trace_utils_unittest.cc",
-      ]
-      deps += [ "../../gn:jsoncpp" ]
-    }
+    sources += [
+      "importers/json/json_trace_tokenizer_unittest.cc",
+      "importers/json/json_tracker_unittest.cc",
+      "importers/json/json_utils_unittest.cc",
+    ]
+    deps += [ "../../gn:jsoncpp" ]
+
     if (!is_win) {
       # export_json_unittest.cc uses base::TempFile, which is not supported on
       # windows.
       sources += [ "export_json_unittest.cc" ]
       deps += [
         ":export_json",
-        "../../gn:jsoncpp",
         "../../include/perfetto/ext/trace_processor:export_json",
       ]
     }
   }
-  if (enable_perfetto_trace_processor_fuchsia) {
-    sources += [ "importers/fuchsia/fuchsia_trace_utils_unittest.cc" ]
-  }
 }
 
 source_set("integrationtests") {
@@ -458,17 +414,20 @@
   sources = []
   deps = []
   if (enable_perfetto_trace_processor_sqlite) {
-    sources += [ "trace_database_integrationtest.cc" ]
+    sources += [
+      "read_trace_integrationtest.cc",
+      "trace_database_integrationtest.cc",
+    ]
     deps += [
       ":lib",
-      ":storage_full",
       "../../gn:default_deps",
       "../../gn:gtest_and_gmock",
+      "../../protos/perfetto/trace:zero",
       "../base",
       "../base:test_support",
       "sqlite",
     ]
-    if (enable_perfetto_trace_processor_json_import) {
+    if (enable_perfetto_trace_processor_json) {
       deps += [ "../../gn:jsoncpp" ]
     }
   }
@@ -477,9 +436,7 @@
 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",
@@ -494,9 +451,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/analysis/BUILD.gn b/src/trace_processor/analysis/BUILD.gn
new file mode 100644
index 0000000..0846610
--- /dev/null
+++ b/src/trace_processor/analysis/BUILD.gn
@@ -0,0 +1,28 @@
+# 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")
+
+source_set("analysis") {
+  sources = [
+    "describe_slice.cc",
+    "describe_slice.h",
+  ]
+  deps = [
+    "../../../gn:default_deps",
+    "../../../include/perfetto/ext/base",
+    "../../../include/perfetto/trace_processor",
+    "../tables",
+  ]
+}
diff --git a/src/trace_processor/analysis/describe_slice.cc b/src/trace_processor/analysis/describe_slice.cc
new file mode 100644
index 0000000..7ad3533
--- /dev/null
+++ b/src/trace_processor/analysis/describe_slice.cc
@@ -0,0 +1,58 @@
+/*
+ * 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/analysis/describe_slice.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+util::Status DescribeSlice(const tables::SliceTable& table,
+                           tables::SliceTable::Id id,
+                           base::Optional<SliceDescription>* description) {
+  auto opt_row = table.id().IndexOf(id);
+  if (!opt_row)
+    return util::ErrStatus("Unable to find slice id");
+
+  uint32_t row = *opt_row;
+
+  base::StringView name = table.name().GetString(row);
+  if (name == "inflate") {
+    *description = SliceDescription{
+        "Constructing a View hierarchy from pre-processed XML via "
+        "LayoutInflater#layout. This includes constructing all of the View "
+        "objects in the hierarchy, and applying styled attributes.",
+        ""};
+    return util::OkStatus();
+  }
+
+  if (name == "measure") {
+    *description = SliceDescription{
+        "First of two phases in view hierarchy layout. Views are asked to size "
+        "themselves according to constraints supplied by their parent. Some "
+        "ViewGroups may measure a child more than once to help satisfy their "
+        "own constraints. Nesting ViewGroups that measure children more than "
+        "once can lead to excessive and repeated work.",
+        "https://developer.android.com/reference/android/view/"
+        "View.html#Layout"};
+    return util::OkStatus();
+  }
+
+  *description = base::nullopt;
+  return util::OkStatus();
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/analysis/describe_slice.h b/src/trace_processor/analysis/describe_slice.h
new file mode 100644
index 0000000..9dedeb8
--- /dev/null
+++ b/src/trace_processor/analysis/describe_slice.h
@@ -0,0 +1,44 @@
+/*
+ * 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_ANALYSIS_DESCRIBE_SLICE_H_
+#define SRC_TRACE_PROCESSOR_ANALYSIS_DESCRIBE_SLICE_H_
+
+#include <string>
+
+#include "perfetto/trace_processor/status.h"
+
+#include "src/trace_processor/tables/slice_tables.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+struct SliceDescription {
+  std::string description;
+  std::string doc_link;
+};
+
+// Provides a description and doc link (if available) for the given slice id in
+// the slice table. For a high level overview of this function's use, see
+// /docs/analysis.md.
+util::Status DescribeSlice(const tables::SliceTable&,
+                           tables::SliceTable::Id,
+                           base::Optional<SliceDescription>* description);
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_ANALYSIS_DESCRIBE_SLICE_H_
diff --git a/src/trace_processor/chunked_trace_reader.h b/src/trace_processor/chunked_trace_reader.h
index f09967d..0d487dc 100644
--- a/src/trace_processor/chunked_trace_reader.h
+++ b/src/trace_processor/chunked_trace_reader.h
@@ -39,6 +39,9 @@
   // intermediate buffering lines/protos that span across different chunks.
   // The buffer size is guaranteed to be > 0.
   virtual util::Status Parse(std::unique_ptr<uint8_t[]>, size_t) = 0;
+
+  // Called after the last Parse() call.
+  virtual void NotifyEndOfFile() = 0;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/containers/BUILD.gn b/src/trace_processor/containers/BUILD.gn
index 32e0d28..f9f1466 100644
--- a/src/trace_processor/containers/BUILD.gn
+++ b/src/trace_processor/containers/BUILD.gn
@@ -23,6 +23,7 @@
     "null_term_string_view.h",
     "row_map.cc",
     "row_map.h",
+    "sparse_vector.cc",
     "sparse_vector.h",
     "string_pool.cc",
     "string_pool.h",
diff --git a/src/trace_processor/containers/bit_vector.h b/src/trace_processor/containers/bit_vector.h
index 5ef0f74..bfd8b4f 100644
--- a/src/trace_processor/containers/bit_vector.h
+++ b/src/trace_processor/containers/bit_vector.h
@@ -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 206bc5d..6578e73 100644
--- a/src/trace_processor/containers/bit_vector_benchmark.cc
+++ b/src/trace_processor/containers/bit_vector_benchmark.cc
@@ -143,6 +143,9 @@
   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;
   }
@@ -208,6 +211,28 @@
 }
 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);
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 b8e7ebb..4a78c2c 100644
--- a/src/trace_processor/containers/bit_vector_unittest.cc
+++ b/src/trace_processor/containers/bit_vector_unittest.cc
@@ -399,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 0a3def0..88a3717 100644
--- a/src/trace_processor/containers/row_map.cc
+++ b/src/trace_processor/containers/row_map.cc
@@ -140,8 +140,11 @@
 
 RowMap::RowMap() : RowMap(0, 0) {}
 
-RowMap::RowMap(uint32_t start, uint32_t end)
-    : mode_(Mode::kRange), start_idx_(start), end_idx_(end) {}
+RowMap::RowMap(uint32_t start, uint32_t end, OptimizeFor optimize_for)
+    : mode_(Mode::kRange),
+      start_idx_(start),
+      end_idx_(end),
+      optimize_for_(optimize_for) {}
 
 RowMap::RowMap(BitVector bit_vector)
     : mode_(Mode::kBitVector), bit_vector_(std::move(bit_vector)) {}
diff --git a/src/trace_processor/containers/row_map.h b/src/trace_processor/containers/row_map.h
index a053245..cb39ac1 100644
--- a/src/trace_processor/containers/row_map.h
+++ b/src/trace_processor/containers/row_map.h
@@ -205,13 +205,22 @@
     const RowMap* rm_ = nullptr;
   };
 
+  // Enum to allow users of RowMap to decide whether they want to optimize for
+  // memory usage or for speed of lookups.
+  enum class OptimizeFor {
+    kMemory,
+    kLookupSpeed,
+  };
+
   // Creates an empty RowMap.
   // By default this will be implemented using a range.
   RowMap();
 
   // Creates a RowMap containing the range of rows between |start| and |end|
   // i.e. all rows between |start| (inclusive) and |end| (exclusive).
-  explicit RowMap(uint32_t start, uint32_t end);
+  explicit RowMap(uint32_t start,
+                  uint32_t end,
+                  OptimizeFor optimize_for = OptimizeFor::kMemory);
 
   // Creates a RowMap backed by a BitVector.
   explicit RowMap(BitVector bit_vector);
@@ -250,11 +259,11 @@
     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");
   }
@@ -395,7 +404,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
@@ -441,41 +450,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); }
 
@@ -489,29 +509,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
@@ -559,6 +585,55 @@
     }
   }
 
+  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. Alternatively, if we are optimizing for
+    // lookup speed, we also want to use an index vector.
+    if (is_small_range || index_vector_cost_ub <= bit_vector_cost ||
+        optimize_for_ == OptimizeFor::kLookupSpeed) {
+      // 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);
 
@@ -567,41 +642,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;
@@ -617,6 +668,8 @@
 
   // Only valid when |mode_| == Mode::kIndexVector.
   std::vector<uint32_t> index_vector_;
+
+  OptimizeFor optimize_for_ = OptimizeFor::kMemory;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/containers/row_map_unittest.cc b/src/trace_processor/containers/row_map_unittest.cc
index bd92ae0..fde10d5 100644
--- a/src/trace_processor/containers/row_map_unittest.cc
+++ b/src/trace_processor/containers/row_map_unittest.cc
@@ -348,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/destructible.cc b/src/trace_processor/containers/sparse_vector.cc
similarity index 80%
copy from src/trace_processor/destructible.cc
copy to src/trace_processor/containers/sparse_vector.cc
index 22bcf6a..ec78c6b 100644
--- a/src/trace_processor/destructible.cc
+++ b/src/trace_processor/containers/sparse_vector.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.
@@ -13,12 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include "src/trace_processor/destructible.h"
+
+#include "src/trace_processor/containers/sparse_vector.h"
 
 namespace perfetto {
 namespace trace_processor {
 
-Destructible::~Destructible() = default;
+SparseVectorBase::~SparseVectorBase() = default;
 
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/containers/sparse_vector.h b/src/trace_processor/containers/sparse_vector.h
index 5c803d3..d90fb89 100644
--- a/src/trace_processor/containers/sparse_vector.h
+++ b/src/trace_processor/containers/sparse_vector.h
@@ -28,6 +28,13 @@
 namespace perfetto {
 namespace trace_processor {
 
+// Base class for SparseVector which allows type erasure to be implemented (e.g.
+// allows for std::unique_ptr<SparseVectorBase>).
+class SparseVectorBase {
+ public:
+  virtual ~SparseVectorBase();
+};
+
 // A data structure which compactly stores a list of possibly nullable data.
 //
 // Internally, this class is implemented using a combination of a std::deque
@@ -36,10 +43,14 @@
 // a slight cost (searching the BitVector to find the index into the std::deque)
 // when looking up the data.
 template <typename T>
-class SparseVector {
+class SparseVector : public SparseVectorBase {
  public:
   // Creates an empty SparseVector.
   SparseVector() = default;
+  ~SparseVector() override = default;
+
+  SparseVector(SparseVector&&) = default;
+  SparseVector& operator=(SparseVector&&) noexcept = default;
 
   // Returns the optional value at |idx| or base::nullopt if the value is null.
   base::Optional<T> Get(uint32_t idx) const {
@@ -104,9 +115,6 @@
   explicit SparseVector(const SparseVector&) = delete;
   SparseVector& operator=(const SparseVector&) = delete;
 
-  SparseVector(SparseVector&&) = delete;
-  SparseVector& operator=(SparseVector&&) noexcept = delete;
-
   std::deque<T> data_;
   RowMap valid_;
   uint32_t size_ = 0;
diff --git a/src/trace_processor/db/BUILD.gn b/src/trace_processor/db/BUILD.gn
index be92403..f1a959f 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",
@@ -36,10 +37,13 @@
   testonly = true
   sources = [
     "compare_unittest.cc",
+    "table_unittest.cc",
   ]
   deps = [
     ":lib",
     "../../../gn:default_deps",
     "../../../gn:gtest_and_gmock",
+    "../../base:base",
+    "../tables:tables",
   ]
 }
diff --git a/src/trace_processor/db/column.cc b/src/trace_processor/db/column.cc
index fedfa7c..94a5c19 100644
--- a/src/trace_processor/db/column.cc
+++ b/src/trace_processor/db/column.cc
@@ -32,7 +32,8 @@
              table,
              col_idx,
              row_map_idx,
-             column.sparse_vector_) {}
+             column.sparse_vector_,
+             column.owned_sparse_vector_) {}
 
 Column::Column(const char* name,
                ColumnType type,
@@ -40,8 +41,10 @@
                Table* table,
                uint32_t col_idx_in_table,
                uint32_t row_map_idx,
-               void* sparse_vector)
-    : type_(type),
+               SparseVectorBase* sparse_vector,
+               std::shared_ptr<SparseVectorBase> owned_sparse_vector)
+    : owned_sparse_vector_(owned_sparse_vector),
+      type_(type),
       sparse_vector_(sparse_vector),
       name_(name),
       flags_(flags),
@@ -51,8 +54,8 @@
       string_pool_(table->string_pool_) {}
 
 Column Column::IdColumn(Table* table, uint32_t col_idx, uint32_t row_map_idx) {
-  return Column("id", ColumnType::kId, Flag::kSorted | Flag::kNonNull, table,
-                col_idx, row_map_idx, nullptr);
+  return Column("id", ColumnType::kId, kIdFlags, table, col_idx, row_map_idx,
+                nullptr, nullptr);
 }
 
 void Column::StableSort(bool desc, std::vector<uint32_t>* idx) const {
@@ -239,10 +242,6 @@
         return cmp(sparse_vector<T>().GetNonNull(idx)) >= 0;
       });
       break;
-    case FilterOp::kGlob:
-    case FilterOp::kLike:
-      rm->Intersect(RowMap());
-      break;
     case FilterOp::kIsNull:
     case FilterOp::kIsNotNull:
       PERFETTO_FATAL("Should be handled above");
@@ -313,12 +312,6 @@
         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/glob constraint on string column");
-      break;
     case FilterOp::kIsNull:
     case FilterOp::kIsNotNull:
       PERFETTO_FATAL("Should be handled above");
@@ -374,10 +367,6 @@
         return compare::Numeric(idx, id_value) >= 0;
       });
       break;
-    case FilterOp::kGlob:
-    case FilterOp::kLike:
-      rm->Intersect(RowMap());
-      break;
     case FilterOp::kIsNull:
     case FilterOp::kIsNotNull:
       PERFETTO_FATAL("Should be handled above");
@@ -454,8 +443,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 17ebe2d..24ff8f5 100644
--- a/src/trace_processor/db/column.h
+++ b/src/trace_processor/db/column.h
@@ -30,6 +30,19 @@
 namespace perfetto {
 namespace trace_processor {
 
+// Id type which can be used as a base for strongly typed ids.
+// TypedColumn has support for storing descendents of BaseId seamlessly
+// in a Column.
+struct BaseId {
+  BaseId() = default;
+  explicit constexpr BaseId(uint32_t v) : value(v) {}
+
+  bool operator==(const BaseId& o) const { return o.value == value; }
+  bool operator<(const BaseId& o) const { return value < o.value; }
+
+  uint32_t value;
+};
+
 // Represents the possible filter operations on a column.
 enum class FilterOp {
   kEq,
@@ -40,8 +53,6 @@
   kLe,
   kIsNull,
   kIsNotNull,
-  kLike,
-  kGlob,
 };
 
 // Represents a constraint on a column.
@@ -85,8 +96,17 @@
     // This is used to speed up filters as we can safely index SparseVector
     // directly if this flag is set.
     kNonNull = 1 << 1,
+
+    // Indicates that the data in the column is "hidden". This can by used to
+    // hint to users of Table and Column that this column should not be
+    // displayed to the user as it is part of the internal implementation
+    // details of the table.
+    kHidden = 1 << 2,
   };
 
+  // Flags specified for an id column.
+  static constexpr uint32_t kIdFlags = Flag::kSorted | Flag::kNonNull;
+
   template <typename T>
   Column(const char* name,
          SparseVector<T>* storage,
@@ -100,7 +120,8 @@
                table,
                col_idx_in_table,
                row_map_idx,
-               storage) {}
+               storage,
+               nullptr) {}
 
   // Create a Column has the same name and is backed by the same data as
   // |column| but is associated to a different table.
@@ -113,6 +134,18 @@
   Column(Column&&) noexcept = default;
   Column& operator=(Column&&) = default;
 
+  template <typename T>
+  static Column WithOwnedStorage(const char* name,
+                                 std::unique_ptr<SparseVector<T>> storage,
+                                 /* Flag */ uint32_t flags,
+                                 Table* table,
+                                 uint32_t col_idx_in_table,
+                                 uint32_t row_map_idx) {
+    SparseVector<T>* ptr = storage.get();
+    return Column(name, ToColumnType<T>(), flags, table, col_idx_in_table,
+                  row_map_idx, ptr, std::move(storage));
+  }
+
   // Creates a Column which returns the index as the value of the row.
   static Column IdColumn(Table* table,
                          uint32_t col_idx_in_table,
@@ -257,19 +290,12 @@
   const char* name() const { return name_; }
 
   // Returns the type of this Column in terms of SqlValue::Type.
-  SqlValue::Type type() const {
-    switch (type_) {
-      case ColumnType::kInt32:
-      case ColumnType::kUint32:
-      case ColumnType::kInt64:
-      case ColumnType::kId:
-        return SqlValue::Type::kLong;
-      case ColumnType::kDouble:
-        return SqlValue::Type::kDouble;
-      case ColumnType::kString:
-        return SqlValue::Type::kString;
-    }
-    PERFETTO_FATAL("For GCC");
+  SqlValue::Type type() const { return ToSqlValueType(type_); }
+
+  // Test the type of this Column.
+  template <typename T>
+  bool IsColumnType() const {
+    return ToColumnType<T>() == type_;
   }
 
   // Returns the index of the current column in the containing table.
@@ -309,13 +335,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>
@@ -332,15 +351,10 @@
     return *static_cast<const SparseVector<T>*>(sparse_vector_);
   }
 
-  // Converts a primitive numeric value to an SqlValue of the correct type.
+  // Returns the type of this Column in terms of SqlValue::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");
+  static SqlValue::Type ToSqlValueType() {
+    return ToSqlValueType(ToColumnType<T>());
   }
 
   const StringPool& string_pool() const { return *string_pool_; }
@@ -410,7 +424,8 @@
          Table* table,
          uint32_t col_idx_in_table,
          uint32_t row_map_idx,
-         void* sparse_vector);
+         SparseVectorBase* sparse_vector,
+         std::shared_ptr<SparseVectorBase> owned_sparse_vector);
 
   Column(const Column&) = delete;
   Column& operator=(const Column&) = delete;
@@ -485,11 +500,9 @@
         rm->Intersect(RowMap(beg, row_map().size()));
         return true;
       }
-      case FilterOp::kGlob:
       case FilterOp::kNe:
       case FilterOp::kIsNull:
       case FilterOp::kIsNotNull:
-      case FilterOp::kLike:
         break;
     }
     return false;
@@ -541,9 +554,36 @@
     }
   }
 
+  static SqlValue::Type ToSqlValueType(ColumnType type) {
+    switch (type) {
+      case ColumnType::kInt32:
+      case ColumnType::kUint32:
+      case ColumnType::kInt64:
+      case ColumnType::kId:
+        return SqlValue::Type::kLong;
+      case ColumnType::kDouble:
+        return SqlValue::Type::kDouble;
+      case ColumnType::kString:
+        return SqlValue::Type::kString;
+    }
+    PERFETTO_FATAL("For GCC");
+  }
+
+  // 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));
+  }
+
+  // Only filled for columns which own the data inside them. Generally this is
+  // only true for columns which are dynamically generated at runtime.
+  // Keep this before |sparse_vector_|.
+  std::shared_ptr<SparseVectorBase> owned_sparse_vector_;
+
   // type_ is used to cast sparse_vector_ to the correct type.
   ColumnType type_ = ColumnType::kInt64;
-  void* sparse_vector_ = nullptr;
+  SparseVectorBase* sparse_vector_ = nullptr;
 
   const char* name_ = nullptr;
   uint32_t flags_ = Flag::kNoFlag;
diff --git a/src/trace_processor/db/table.cc b/src/trace_processor/db/table.cc
index 6fa723c..ebcdf9e 100644
--- a/src/trace_processor/db/table.cc
+++ b/src/trace_processor/db/table.cc
@@ -19,6 +19,9 @@
 namespace perfetto {
 namespace trace_processor {
 
+Table::Table() = default;
+Table::~Table() = default;
+
 Table::Table(StringPool* pool, const Table* parent) : string_pool_(pool) {
   if (!parent)
     return;
diff --git a/src/trace_processor/db/table.h b/src/trace_processor/db/table.h
index 058dac7..91d1e65 100644
--- a/src/trace_processor/db/table.h
+++ b/src/trace_processor/db/table.h
@@ -27,6 +27,7 @@
 #include "perfetto/ext/base/optional.h"
 #include "src/trace_processor/containers/string_pool.h"
 #include "src/trace_processor/db/column.h"
+#include "src/trace_processor/db/typed_column.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -70,21 +71,46 @@
     std::vector<RowMap::Iterator> its_;
   };
 
+  // Helper class storing the schema of the table. This allows decisions to be
+  // made about operations on the table without materializing the table - this
+  // may be expensive for dynamically computed tables.
+  //
+  // Subclasses of Table usually provide a method (named Schema()) to statically
+  // generate an instance of this class.
+  struct Schema {
+    struct Column {
+      std::string name;
+      SqlValue::Type type;
+      bool is_id;
+      bool is_sorted;
+      bool is_hidden;
+    };
+    std::vector<Column> columns;
+  };
+
+  Table();
+  virtual ~Table();
+
   // We explicitly define the move constructor here because we need to update
   // the Table pointer in each column in the table.
   Table(Table&& other) noexcept { *this = std::move(other); }
   Table& operator=(Table&& other) noexcept;
 
   // Filters the Table using the specified filter constraints.
-  Table Filter(const std::vector<Constraint>& cs) const {
-    return Apply(FilterToRowMap(cs));
+  Table Filter(
+      const std::vector<Constraint>& cs,
+      RowMap::OptimizeFor optimize_for = RowMap::OptimizeFor::kMemory) const {
+    return Apply(FilterToRowMap(cs, optimize_for));
   }
 
-  // Filters the Table using the specified filter constraints.
+  // Filters the Table using the specified filter constraints optionally
+  // specifying what the returned RowMap should optimize for.
   // Returns a RowMap which, if applied to the table, would contain the rows
   // post filter.
-  RowMap FilterToRowMap(const std::vector<Constraint>& cs) const {
-    RowMap rm(0, row_count_);
+  RowMap FilterToRowMap(
+      const std::vector<Constraint>& cs,
+      RowMap::OptimizeFor optimize_for = RowMap::OptimizeFor::kMemory) const {
+    RowMap rm(0, row_count_, optimize_for);
     for (const Constraint& c : cs) {
       columns_[c.col_idx].FilterInto(c.op, c.value, &rm);
     }
@@ -124,6 +150,20 @@
   //  * |left|'s values must exist in |right|
   Table LookupJoin(JoinKey left, const Table& other, JoinKey right);
 
+  template <typename T>
+  Table ExtendWithColumn(const char* name,
+                         std::unique_ptr<SparseVector<T>> sv,
+                         uint32_t flags) const {
+    PERFETTO_DCHECK(sv->size() == row_count_);
+    uint32_t size = sv->size();
+    uint32_t row_map_count = static_cast<uint32_t>(row_maps_.size());
+    Table ret = Copy();
+    ret.columns_.push_back(Column::WithOwnedStorage(
+        name, std::move(sv), flags, &ret, GetColumnCount(), row_map_count));
+    ret.row_maps_.emplace_back(RowMap(0, size));
+    return ret;
+  }
+
   // Returns the column at index |idx| in the Table.
   const Column& GetColumn(uint32_t idx) const { return columns_[idx]; }
 
@@ -137,6 +177,11 @@
     return &*it;
   }
 
+  template <typename T>
+  const TypedColumn<T>* GetTypedColumnByName(const char* name) const {
+    return TypedColumn<T>::FromColumn(GetColumnByName(name));
+  }
+
   // Returns the number of columns in the Table.
   uint32_t GetColumnCount() const {
     return static_cast<uint32_t>(columns_.size());
diff --git a/src/trace_processor/db/table_unittest.cc b/src/trace_processor/db/table_unittest.cc
new file mode 100644
index 0000000..21e0c80
--- /dev/null
+++ b/src/trace_processor/db/table_unittest.cc
@@ -0,0 +1,69 @@
+/*
+ * 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/db/table.h"
+#include "perfetto/ext/base/optional.h"
+#include "src/trace_processor/db/typed_column.h"
+#include "src/trace_processor/tables/macros.h"
+
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+constexpr uint32_t kColumnCount = 1024;
+
+std::unique_ptr<SparseVector<int64_t>> Column() {
+  auto c = std::unique_ptr<SparseVector<int64_t>>(new SparseVector<int64_t>());
+  for (int64_t i = 0; i < kColumnCount; ++i)
+    c->Append(i);
+  return c;
+}
+
+uint32_t Flags() {
+  return TypedColumn<int64_t>::default_flags();
+}
+
+#define PERFETTO_TP_TEST_EVENT_TABLE_DEF(NAME, PARENT, C) \
+  NAME(TestEventTable, "event")                           \
+  PARENT(PERFETTO_TP_ROOT_TABLE_PARENT_DEF, C)            \
+  C(int64_t, ts, Column::Flag::kSorted)                   \
+  C(int64_t, arg_set_id)
+PERFETTO_TP_TABLE(PERFETTO_TP_TEST_EVENT_TABLE_DEF);
+
+TestEventTable::~TestEventTable() = default;
+
+TEST(TableTest, ExtendingTableTwice) {
+  StringPool pool;
+  TestEventTable table{&pool, nullptr};
+
+  for (uint32_t i = 0; i < kColumnCount; ++i)
+    table.Insert(TestEventTable::Row(i));
+
+  Table filtered_table;
+  {
+    filtered_table = table.ExtendWithColumn("a", Column(), Flags())
+                         .ExtendWithColumn("b", Column(), Flags())
+                         .Filter({});
+  }
+  ASSERT_TRUE(filtered_table.GetColumnByName("a")->Max().has_value());
+  ASSERT_TRUE(filtered_table.GetColumnByName("b")->Max().has_value());
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/db/typed_column.h b/src/trace_processor/db/typed_column.h
index 5be1dcc..e96a133 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,145 @@
 #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;
+
+  get_type operator[](uint32_t row) const {
+    return Serializer::Deserialize(
+        TH::Get(sparse_vector(), 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(sparse_vector().GetNonNull(row_map().Get(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;
+  }
+
+  // Converts the static type T into the dynamic SqlValue type of this column.
+  static SqlValue::Type SqlValueType() {
+    return Column::ToSqlValueType<serialized_type>();
+  }
+
+  // Reinterpret cast a Column to TypedColumn or crash if that is likely to be
+  // unsafe.
+  static const TypedColumn<T>* FromColumn(const Column* column) {
+    if (column->IsColumnType<serialized_type>() &&
+        (column->IsNullable() == TH::is_optional) && !column->IsId()) {
+      return reinterpret_cast<const TypedColumn<T>*>(column);
+    } else {
+      PERFETTO_FATAL("Unsafe to convert Column to TypedColumn.");
+    }
+  }
+
+ 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,182 +173,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));
-  }
-
-  std::vector<T> 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;
-  }
-
-  // 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); }
-
-  std::vector<base::Optional<T>> 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;
-  }
-
-  // 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::Null());
-  }
-
-  // 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..07c8e8a
--- /dev/null
+++ b/src/trace_processor/db/typed_column_internal.h
@@ -0,0 +1,195 @@
+/*
+ * 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;
+  }
+};
+
+template <typename T>
+using is_id = std::is_base_of<BaseId, T>;
+
+// Specialization of Serializer for id types.
+template <typename T>
+struct Serializer<T, typename std::enable_if<is_id<T>::value>::type> {
+  using serialized_type = uint32_t;
+
+  static serialized_type Serialize(T value) { return value.value; }
+  static T Deserialize(serialized_type value) { return T{value}; }
+
+  static base::Optional<serialized_type> Serialize(base::Optional<T> value) {
+    return value ? base::make_optional(Serialize(*value)) : base::nullopt;
+  }
+  static base::Optional<T> Deserialize(base::Optional<serialized_type> value) {
+    return value ? base::make_optional(Deserialize(*value)) : base::nullopt;
+  }
+};
+
+// 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> value) {
+    return value;
+  }
+};
+
+// 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;
+
+  template <typename SerializedType>
+  static SerializedType Get(const SparseVector<SerializedType>& sv,
+                            uint32_t idx) {
+    return sv.GetNonNull(idx);
+  }
+
+  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;
+
+  template <typename SerializedType>
+  static base::Optional<SerializedType> Get(
+      const SparseVector<SerializedType>& sv,
+      uint32_t idx) {
+    return sv.Get(idx);
+  }
+
+  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 StringPool::Id Get(const SparseVector<StringPool::Id>& sv,
+                            uint32_t idx) {
+    return sv.GetNonNull(idx);
+  }
+
+  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 = base::Optional<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 base::Optional<StringPool::Id> Get(
+      const SparseVector<StringPool::Id>& sv,
+      uint32_t idx) {
+    StringPool::Id id = sv.GetNonNull(idx);
+    return id.is_null() ? base::nullopt : base::make_optional(id);
+  }
+
+  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/dynamic/describe_slice_generator.cc b/src/trace_processor/dynamic/describe_slice_generator.cc
new file mode 100644
index 0000000..edc610d
--- /dev/null
+++ b/src/trace_processor/dynamic/describe_slice_generator.cc
@@ -0,0 +1,113 @@
+/*
+ * 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/dynamic/describe_slice_generator.h"
+
+#include "src/trace_processor/analysis/describe_slice.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+namespace {
+
+DescribeSliceGenerator::InputValues GetDescribeSliceInputValues(
+    const std::vector<Constraint>& cs) {
+  using DST = tables::DescribeSliceTable;
+
+  auto slice_id_fn = [](const Constraint& c) {
+    return c.col_idx == static_cast<uint32_t>(DST::ColumnIndex::slice_id) &&
+           c.op == FilterOp::kEq;
+  };
+  auto slice_id_it = std::find_if(cs.begin(), cs.end(), slice_id_fn);
+
+  // We should always have valid iterators 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(slice_id_it != cs.end());
+
+  uint32_t slice_id_value = static_cast<uint32_t>(slice_id_it->value.AsLong());
+  return DescribeSliceGenerator::InputValues{slice_id_value};
+}
+
+}  // namespace
+
+DescribeSliceGenerator::DescribeSliceGenerator(TraceProcessorContext* context)
+    : context_(context) {}
+
+DescribeSliceGenerator::~DescribeSliceGenerator() = default;
+
+util::Status DescribeSliceGenerator::ValidateConstraints(
+    const QueryConstraints& qc) {
+  using T = tables::DescribeSliceTable;
+
+  const auto& cs = qc.constraints();
+
+  auto slice_id_fn = [](const QueryConstraints::Constraint& c) {
+    return c.column == static_cast<int>(T::ColumnIndex::slice_id) &&
+           c.op == SQLITE_INDEX_CONSTRAINT_EQ;
+  };
+  bool has_slice_id_cs =
+      std::find_if(cs.begin(), cs.end(), slice_id_fn) != cs.end();
+
+  return has_slice_id_cs
+             ? util::OkStatus()
+             : util::ErrStatus("Failed to find required constraints");
+}
+
+std::unique_ptr<Table> DescribeSliceGenerator::ComputeTable(
+    const std::vector<Constraint>& cs,
+    const std::vector<Order>&) {
+  auto input = GetDescribeSliceInputValues(cs);
+  const auto& slices = context_->storage->slice_table();
+
+  base::Optional<SliceDescription> opt_desc;
+  auto status = DescribeSlice(slices, SliceId{input.slice_id_value}, &opt_desc);
+  if (!status.ok())
+    return nullptr;
+
+  auto* pool = context_->storage->mutable_string_pool();
+  std::unique_ptr<tables::DescribeSliceTable> table(
+      new tables::DescribeSliceTable(pool, nullptr));
+
+  if (opt_desc) {
+    tables::DescribeSliceTable::Row row;
+    row.description = context_->storage->InternString(
+        base::StringView(opt_desc->description));
+    row.doc_link =
+        context_->storage->InternString(base::StringView(opt_desc->doc_link));
+    row.slice_id = input.slice_id_value;
+    table->Insert(row);
+  }
+  // We need to explicitly std::move as clang complains about a bug in old
+  // compilers otherwise (-Wreturn-std-move-in-c++11).
+  return std::move(table);
+}
+
+Table::Schema DescribeSliceGenerator::CreateSchema() {
+  return tables::DescribeSliceTable::Schema();
+}
+
+std::string DescribeSliceGenerator::TableName() {
+  return "describe_slice";
+}
+
+uint32_t DescribeSliceGenerator::EstimateRowCount() {
+  return 1;
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/dynamic/describe_slice_generator.h b/src/trace_processor/dynamic/describe_slice_generator.h
new file mode 100644
index 0000000..af59529
--- /dev/null
+++ b/src/trace_processor/dynamic/describe_slice_generator.h
@@ -0,0 +1,55 @@
+/*
+ * 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_DYNAMIC_DESCRIBE_SLICE_GENERATOR_H_
+#define SRC_TRACE_PROCESSOR_DYNAMIC_DESCRIBE_SLICE_GENERATOR_H_
+
+#include "src/trace_processor/sqlite/db_sqlite_table.h"
+
+#include "src/trace_processor/storage/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+// Dynamic table for implementing the describe_slice table.
+// See /docs/analysis.md for details about the functionality and usage of this
+// table.
+class DescribeSliceGenerator : public DbSqliteTable::DynamicTableGenerator {
+ public:
+  struct InputValues {
+    uint32_t slice_id_value;
+  };
+
+  explicit DescribeSliceGenerator(TraceProcessorContext* context);
+  ~DescribeSliceGenerator() override;
+
+  Table::Schema CreateSchema() override;
+  std::string TableName() override;
+  uint32_t EstimateRowCount() override;
+  util::Status ValidateConstraints(const QueryConstraints&) override;
+  std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>& cs,
+                                      const std::vector<Order>& ob) override;
+
+ private:
+  TraceProcessorContext* context_ = nullptr;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_DYNAMIC_DESCRIBE_SLICE_GENERATOR_H_
diff --git a/src/trace_processor/dynamic/experimental_counter_dur_generator.cc b/src/trace_processor/dynamic/experimental_counter_dur_generator.cc
new file mode 100644
index 0000000..b5db405
--- /dev/null
+++ b/src/trace_processor/dynamic/experimental_counter_dur_generator.cc
@@ -0,0 +1,102 @@
+/*
+ * 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/dynamic/experimental_counter_dur_generator.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+ExperimentalCounterDurGenerator::ExperimentalCounterDurGenerator(
+    const tables::CounterTable& table)
+    : counter_table_(&table) {}
+ExperimentalCounterDurGenerator::~ExperimentalCounterDurGenerator() = default;
+
+Table::Schema ExperimentalCounterDurGenerator::CreateSchema() {
+  Table::Schema schema = tables::CounterTable::Schema();
+  schema.columns.emplace_back(
+      Table::Schema::Column{"dur", SqlValue::Type::kLong, false /* is_id */,
+                            false /* is_sorted */, false /* is_hidden */});
+  return schema;
+}
+
+std::string ExperimentalCounterDurGenerator::TableName() {
+  return "experimental_counter_dur";
+}
+
+uint32_t ExperimentalCounterDurGenerator::EstimateRowCount() {
+  return counter_table_->row_count();
+}
+
+util::Status ExperimentalCounterDurGenerator::ValidateConstraints(
+    const QueryConstraints&) {
+  return util::OkStatus();
+}
+
+std::unique_ptr<Table> ExperimentalCounterDurGenerator::ComputeTable(
+    const std::vector<Constraint>& cs,
+    const std::vector<Order>&) {
+  std::vector<Constraint> constraints;
+  for (const auto& c : cs) {
+    if (c.col_idx ==
+        static_cast<uint32_t>(tables::CounterTable::ColumnIndex::track_id)) {
+      constraints.push_back(c);
+    }
+  }
+  Table table = counter_table_->Filter(constraints);
+
+  std::unique_ptr<SparseVector<int64_t>> dur_column(
+      new SparseVector<int64_t>(ComputeDurColumn(table)));
+  return std::unique_ptr<Table>(new Table(table.ExtendWithColumn(
+      "dur", std::move(dur_column), TypedColumn<int64_t>::default_flags())));
+}
+
+// static
+SparseVector<int64_t> ExperimentalCounterDurGenerator::ComputeDurColumn(
+    const Table& table) {
+  // Keep track of the last seen row for each track id.
+  std::unordered_map<TrackId, uint32_t> last_row_for_track_id;
+  SparseVector<int64_t> dur;
+
+  const auto* ts_col =
+      TypedColumn<int64_t>::FromColumn(table.GetColumnByName("ts"));
+  const auto* track_id_col =
+      TypedColumn<tables::CounterTrackTable::Id>::FromColumn(
+          table.GetColumnByName("track_id"));
+
+  for (uint32_t i = 0; i < table.row_count(); ++i) {
+    // Check if we already have a previous row for the current track id.
+    TrackId track_id = (*track_id_col)[i];
+    auto it = last_row_for_track_id.find(track_id);
+    if (it == last_row_for_track_id.end()) {
+      // This means we don't have any row - start tracking this row for the
+      // future.
+      last_row_for_track_id.emplace(track_id, i);
+    } else {
+      // This means we have an previous row for the current track id. Update
+      // the duration of the previous row to be up to the current ts.
+      uint32_t old_row = it->second;
+      it->second = i;
+      dur.Set(old_row, (*ts_col)[i] - (*ts_col)[old_row]);
+    }
+    // Append -1 to mark this event as not having been finished. On a later
+    // row, we may set this to have the correct value.
+    dur.Append(-1);
+  }
+  return dur;
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/dynamic/experimental_counter_dur_generator.h b/src/trace_processor/dynamic/experimental_counter_dur_generator.h
new file mode 100644
index 0000000..6e88762
--- /dev/null
+++ b/src/trace_processor/dynamic/experimental_counter_dur_generator.h
@@ -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.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_DYNAMIC_EXPERIMENTAL_COUNTER_DUR_GENERATOR_H_
+#define SRC_TRACE_PROCESSOR_DYNAMIC_EXPERIMENTAL_COUNTER_DUR_GENERATOR_H_
+
+#include "src/trace_processor/sqlite/db_sqlite_table.h"
+
+#include "src/trace_processor/storage/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class ExperimentalCounterDurGenerator
+    : public DbSqliteTable::DynamicTableGenerator {
+ public:
+  explicit ExperimentalCounterDurGenerator(const tables::CounterTable& table);
+  virtual ~ExperimentalCounterDurGenerator() override;
+
+  Table::Schema CreateSchema() override;
+  std::string TableName() override;
+  uint32_t EstimateRowCount() override;
+  util::Status ValidateConstraints(const QueryConstraints&) override;
+  std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>&,
+                                      const std::vector<Order>&) override;
+
+  // public + static for testing
+  static SparseVector<int64_t> ComputeDurColumn(const Table& table);
+
+ private:
+  const tables::CounterTable* counter_table_ = nullptr;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_DYNAMIC_EXPERIMENTAL_COUNTER_DUR_GENERATOR_H_
diff --git a/src/trace_processor/dynamic/experimental_counter_dur_generator_unittest.cc b/src/trace_processor/dynamic/experimental_counter_dur_generator_unittest.cc
new file mode 100644
index 0000000..ffe3db1
--- /dev/null
+++ b/src/trace_processor/dynamic/experimental_counter_dur_generator_unittest.cc
@@ -0,0 +1,56 @@
+/*
+ * 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/dynamic/experimental_counter_dur_generator.h"
+
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+tables::CounterTable::Row CounterRow(int64_t ts, uint32_t track_id) {
+  tables::CounterTable::Row row;
+  row.ts = ts;
+  row.track_id = tables::TrackTable::Id{track_id};
+  return row;
+}
+
+TEST(ExperimentalCounterDurGenerator, SmokeDur) {
+  StringPool pool;
+  tables::CounterTable table(&pool, nullptr);
+
+  table.Insert(CounterRow(100 /* ts */, 1 /* track_id */));
+  table.Insert(CounterRow(102 /* ts */, 2 /* track_id */));
+  table.Insert(CounterRow(105 /* ts */, 1 /* track_id */));
+  table.Insert(CounterRow(105 /* ts */, 3 /* track_id */));
+  table.Insert(CounterRow(105 /* ts */, 2 /* track_id */));
+  table.Insert(CounterRow(110 /* ts */, 2 /* track_id */));
+
+  auto dur = ExperimentalCounterDurGenerator::ComputeDurColumn(table);
+  ASSERT_EQ(dur.size(), table.row_count());
+
+  ASSERT_EQ(dur.GetNonNull(0), 5);
+  ASSERT_EQ(dur.GetNonNull(1), 3);
+  ASSERT_EQ(dur.GetNonNull(2), -1);
+  ASSERT_EQ(dur.GetNonNull(3), -1);
+  ASSERT_EQ(dur.GetNonNull(4), 5);
+  ASSERT_EQ(dur.GetNonNull(5), -1);
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/dynamic/experimental_flamegraph_generator.cc b/src/trace_processor/dynamic/experimental_flamegraph_generator.cc
new file mode 100644
index 0000000..0d4e81e
--- /dev/null
+++ b/src/trace_processor/dynamic/experimental_flamegraph_generator.cc
@@ -0,0 +1,296 @@
+/*
+ * 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/dynamic/experimental_flamegraph_generator.h"
+
+#include "perfetto/ext/base/string_utils.h"
+
+#include "src/trace_processor/importers/proto/heap_graph_tracker.h"
+#include "src/trace_processor/importers/proto/heap_profile_tracker.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+namespace {
+
+ExperimentalFlamegraphGenerator::InputValues GetFlamegraphInputValues(
+    const std::vector<Constraint>& cs) {
+  using T = tables::ExperimentalFlamegraphNodesTable;
+
+  auto ts_fn = [](const Constraint& c) {
+    return c.col_idx == static_cast<uint32_t>(T::ColumnIndex::ts) &&
+           c.op == FilterOp::kEq;
+  };
+  auto upid_fn = [](const Constraint& c) {
+    return c.col_idx == static_cast<uint32_t>(T::ColumnIndex::upid) &&
+           c.op == FilterOp::kEq;
+  };
+  auto profile_type_fn = [](const Constraint& c) {
+    return c.col_idx == static_cast<uint32_t>(T::ColumnIndex::profile_type) &&
+           c.op == FilterOp::kEq;
+  };
+  auto focus_str_fn = [](const Constraint& c) {
+    return c.col_idx == static_cast<uint32_t>(T::ColumnIndex::focus_str) &&
+           c.op == FilterOp::kEq;
+  };
+
+  auto ts_it = std::find_if(cs.begin(), cs.end(), ts_fn);
+  auto upid_it = std::find_if(cs.begin(), cs.end(), upid_fn);
+  auto profile_type_it = std::find_if(cs.begin(), cs.end(), profile_type_fn);
+  auto focus_str_it = std::find_if(cs.begin(), cs.end(), focus_str_fn);
+
+  // We should always have valid iterators 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_it != cs.end());
+  PERFETTO_CHECK(upid_it != cs.end());
+  PERFETTO_CHECK(profile_type_it != cs.end());
+
+  int64_t ts = ts_it->value.AsLong();
+  UniquePid upid = static_cast<UniquePid>(upid_it->value.AsLong());
+  std::string profile_type = profile_type_it->value.AsString();
+  std::string focus_str =
+      focus_str_it != cs.end() ? focus_str_it->value.AsString() : "";
+  return ExperimentalFlamegraphGenerator::InputValues{ts, upid, profile_type,
+                                                      focus_str};
+}
+
+class Matcher {
+ public:
+  explicit Matcher(const std::string& str) : focus_str_(base::ToLower(str)) {}
+  Matcher(const Matcher&) = delete;
+  Matcher& operator=(const Matcher&) = delete;
+
+  bool matches(const std::string& s) const {
+    // TODO(149833691): change to regex.
+    // We cannot use regex.h (does not exist in windows) or std regex (throws
+    // exceptions).
+    return base::Contains(base::ToLower(s), focus_str_);
+  }
+
+ private:
+  const std::string focus_str_;
+};
+
+enum class FocusedState {
+  kNotFocused,
+  kFocusedPropagating,
+  kFocusedNotPropagating,
+};
+
+using tables::ExperimentalFlamegraphNodesTable;
+std::vector<FocusedState> ComputeFocusedState(
+    const ExperimentalFlamegraphNodesTable& table,
+    const Matcher& focus_matcher) {
+  // Each row corresponds to a node in the flame chart tree with its parent
+  // ptr. Root trees (no parents) will have a null parent ptr.
+  std::vector<FocusedState> focused(table.row_count());
+
+  for (uint32_t i = 0; i < table.row_count(); ++i) {
+    auto parent_id = table.parent_id()[i];
+    // Constraint: all descendants MUST come after their parents.
+    PERFETTO_DCHECK(!parent_id.has_value() || *parent_id < table.id()[i]);
+
+    if (focus_matcher.matches(table.name().GetString(i).ToStdString())) {
+      // Mark as focused
+      focused[i] = FocusedState::kFocusedPropagating;
+      auto current = parent_id;
+      // Mark all parent nodes as focused
+      while (current.has_value()) {
+        auto current_idx = *table.id().IndexOf(*current);
+        if (focused[current_idx] != FocusedState::kNotFocused) {
+          // We have already visited these nodes, skip
+          break;
+        }
+        focused[current_idx] = FocusedState::kFocusedNotPropagating;
+        current = table.parent_id()[current_idx];
+      }
+    } else if (parent_id.has_value() &&
+               focused[*table.id().IndexOf(*parent_id)] ==
+                   FocusedState::kFocusedPropagating) {
+      // Focus cascades downwards.
+      focused[i] = FocusedState::kFocusedPropagating;
+    } else {
+      focused[i] = FocusedState::kNotFocused;
+    }
+  }
+  return focused;
+}
+
+struct CumulativeCounts {
+  int64_t size;
+  int64_t count;
+  int64_t alloc_size;
+  int64_t alloc_count;
+};
+std::unique_ptr<tables::ExperimentalFlamegraphNodesTable> FocusTable(
+    TraceStorage* storage,
+    std::unique_ptr<ExperimentalFlamegraphNodesTable> in,
+    const std::string& focus_str) {
+  if (in->row_count() == 0 || focus_str.empty()) {
+    return in;
+  }
+  std::vector<FocusedState> focused_state =
+      ComputeFocusedState(*in.get(), Matcher(focus_str));
+  std::unique_ptr<ExperimentalFlamegraphNodesTable> tbl(
+      new tables::ExperimentalFlamegraphNodesTable(
+          storage->mutable_string_pool(), nullptr));
+
+  // Recompute cumulative counts
+  std::vector<CumulativeCounts> node_to_cumulatives(in->row_count());
+  for (int64_t idx = in->row_count() - 1; idx >= 0; --idx) {
+    auto i = static_cast<uint32_t>(idx);
+    if (focused_state[i] == FocusedState::kNotFocused) {
+      continue;
+    }
+    auto& cumulatives = node_to_cumulatives[i];
+    cumulatives.size += in->size()[i];
+    cumulatives.count += in->count()[i];
+    cumulatives.alloc_size += in->alloc_size()[i];
+    cumulatives.alloc_count += in->alloc_count()[i];
+
+    auto parent_id = in->parent_id()[i];
+    if (parent_id.has_value()) {
+      auto& parent_cumulatives =
+          node_to_cumulatives[*in->id().IndexOf(*parent_id)];
+      parent_cumulatives.size += cumulatives.size;
+      parent_cumulatives.count += cumulatives.count;
+      parent_cumulatives.alloc_size += cumulatives.alloc_size;
+      parent_cumulatives.alloc_count += cumulatives.alloc_count;
+    }
+  }
+
+  // Mapping between the old rows ('node') to the new identifiers.
+  std::vector<ExperimentalFlamegraphNodesTable::Id> node_to_id(in->row_count());
+  for (uint32_t i = 0; i < in->row_count(); ++i) {
+    if (focused_state[i] == FocusedState::kNotFocused) {
+      continue;
+    }
+
+    tables::ExperimentalFlamegraphNodesTable::Row alloc_row{};
+    // We must reparent the rows as every insertion will get its own
+    // identifier.
+    auto original_parent_id = in->parent_id()[i];
+    if (original_parent_id.has_value()) {
+      auto original_idx = *in->id().IndexOf(*original_parent_id);
+      alloc_row.parent_id = node_to_id[original_idx];
+    }
+
+    alloc_row.ts = in->ts()[i];
+    alloc_row.upid = in->upid()[i];
+    alloc_row.profile_type = in->profile_type()[i];
+    alloc_row.depth = in->depth()[i];
+    alloc_row.name = in->name()[i];
+    alloc_row.map_name = in->map_name()[i];
+    alloc_row.count = in->count()[i];
+    alloc_row.size = in->size()[i];
+    alloc_row.alloc_count = in->alloc_count()[i];
+    alloc_row.alloc_size = in->alloc_size()[i];
+
+    const auto& cumulative = node_to_cumulatives[i];
+    alloc_row.cumulative_count = cumulative.count;
+    alloc_row.cumulative_size = cumulative.size;
+    alloc_row.cumulative_alloc_count = cumulative.alloc_count;
+    alloc_row.cumulative_alloc_size = cumulative.alloc_size;
+    node_to_id[i] = tbl->Insert(alloc_row).id;
+  }
+  return tbl;
+}
+}  // namespace
+
+ExperimentalFlamegraphGenerator::ExperimentalFlamegraphGenerator(
+    TraceProcessorContext* context)
+    : context_(context) {}
+
+ExperimentalFlamegraphGenerator::~ExperimentalFlamegraphGenerator() = default;
+
+util::Status ExperimentalFlamegraphGenerator::ValidateConstraints(
+    const QueryConstraints& qc) {
+  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
+             ? util::OkStatus()
+             : util::ErrStatus("Failed to find required constraints");
+}
+
+std::unique_ptr<Table> ExperimentalFlamegraphGenerator::ComputeTable(
+    const std::vector<Constraint>& cs,
+    const std::vector<Order>&) {
+  // Get the input column values and compute the flamegraph using them.
+  auto values = GetFlamegraphInputValues(cs);
+
+  std::unique_ptr<tables::ExperimentalFlamegraphNodesTable> table;
+  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);
+  }
+  if (!values.focus_str.empty()) {
+    table =
+        FocusTable(context_->storage.get(), std::move(table), values.focus_str);
+    // The pseudocolumns must be populated because as far as SQLite is
+    // concerned these are equality constraints.
+    auto focus_id =
+        context_->storage->InternString(base::StringView(values.focus_str));
+    for (uint32_t i = 0; i < table->row_count(); ++i) {
+      table->mutable_focus_str()->Set(i, focus_id);
+    }
+  }
+  // We need to explicitly std::move as clang complains about a bug in old
+  // compilers otherwise (-Wreturn-std-move-in-c++11).
+  return std::move(table);
+}
+
+Table::Schema ExperimentalFlamegraphGenerator::CreateSchema() {
+  return tables::ExperimentalFlamegraphNodesTable::Schema();
+}
+
+std::string ExperimentalFlamegraphGenerator::TableName() {
+  return "experimental_flamegraph";
+}
+
+uint32_t ExperimentalFlamegraphGenerator::EstimateRowCount() {
+  // TODO(lalitm): return a better estimate here when possible.
+  return 1024;
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/dynamic/experimental_flamegraph_generator.h b/src/trace_processor/dynamic/experimental_flamegraph_generator.h
new file mode 100644
index 0000000..7ca1f51
--- /dev/null
+++ b/src/trace_processor/dynamic/experimental_flamegraph_generator.h
@@ -0,0 +1,56 @@
+/*
+ * 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_DYNAMIC_EXPERIMENTAL_FLAMEGRAPH_GENERATOR_H_
+#define SRC_TRACE_PROCESSOR_DYNAMIC_EXPERIMENTAL_FLAMEGRAPH_GENERATOR_H_
+
+#include "src/trace_processor/sqlite/db_sqlite_table.h"
+
+#include "src/trace_processor/storage/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+class ExperimentalFlamegraphGenerator
+    : public DbSqliteTable::DynamicTableGenerator {
+ public:
+  struct InputValues {
+    int64_t ts;
+    UniquePid upid;
+    std::string profile_type;
+    std::string focus_str;
+  };
+
+  explicit ExperimentalFlamegraphGenerator(TraceProcessorContext* context);
+  virtual ~ExperimentalFlamegraphGenerator() override;
+
+  Table::Schema CreateSchema() override;
+  std::string TableName() override;
+  uint32_t EstimateRowCount() override;
+  util::Status ValidateConstraints(const QueryConstraints&) override;
+  std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>& cs,
+                                      const std::vector<Order>& ob) override;
+
+ private:
+  TraceProcessorContext* context_ = nullptr;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_DYNAMIC_EXPERIMENTAL_FLAMEGRAPH_GENERATOR_H_
diff --git a/src/trace_processor/dynamic/experimental_slice_layout_generator.cc b/src/trace_processor/dynamic/experimental_slice_layout_generator.cc
new file mode 100644
index 0000000..0045001
--- /dev/null
+++ b/src/trace_processor/dynamic/experimental_slice_layout_generator.cc
@@ -0,0 +1,254 @@
+/*
+ * 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/dynamic/experimental_slice_layout_generator.h"
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "src/trace_processor/sqlite/sqlite_utils.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+struct GroupInfo {
+  GroupInfo(int64_t _start, int64_t _end, uint32_t _max_height)
+      : start(_start), end(_end), max_height(_max_height) {}
+  int64_t start;
+  int64_t end;
+  uint32_t max_height;
+  uint32_t layout_depth;
+};
+
+}  // namespace
+
+ExperimentalSliceLayoutGenerator::ExperimentalSliceLayoutGenerator(
+    StringPool* string_pool,
+    const tables::SliceTable* table)
+    : string_pool_(string_pool),
+      slice_table_(table),
+      empty_string_id_(string_pool_->InternString("")) {}
+ExperimentalSliceLayoutGenerator::~ExperimentalSliceLayoutGenerator() = default;
+
+Table::Schema ExperimentalSliceLayoutGenerator::CreateSchema() {
+  Table::Schema schema = tables::SliceTable::Schema();
+  schema.columns.emplace_back(Table::Schema::Column{
+      "layout_depth", SqlValue::Type::kLong, false /* is_id */,
+      false /* is_sorted */, false /* is_hidden */});
+  schema.columns.emplace_back(Table::Schema::Column{
+      "filter_track_ids", SqlValue::Type::kString, false /* is_id */,
+      false /* is_sorted */, true /* is_hidden */});
+  return schema;
+}
+
+std::string ExperimentalSliceLayoutGenerator::TableName() {
+  return "experimental_slice_layout";
+}
+
+uint32_t ExperimentalSliceLayoutGenerator::EstimateRowCount() {
+  return slice_table_->row_count();
+}
+
+util::Status ExperimentalSliceLayoutGenerator::ValidateConstraints(
+    const QueryConstraints& cs) {
+  for (const auto& c : cs.constraints()) {
+    if (c.column == kFilterTrackIdsColumnIndex && sqlite_utils::IsOpEq(c.op)) {
+      return util::OkStatus();
+    }
+  }
+  return util::ErrStatus(
+      "experimental_slice_layout must have filter_track_ids constraint");
+}
+
+std::unique_ptr<Table> ExperimentalSliceLayoutGenerator::ComputeTable(
+    const std::vector<Constraint>& cs,
+    const std::vector<Order>&) {
+  std::set<TrackId> selected_tracks;
+  std::string filter_string = "";
+  for (const auto& c : cs) {
+    bool is_filter_track_ids = c.col_idx == kFilterTrackIdsColumnIndex;
+    bool is_equal = c.op == FilterOp::kEq;
+    bool is_string = c.value.type == SqlValue::kString;
+    if (is_filter_track_ids && is_equal && is_string) {
+      filter_string = c.value.AsString();
+      for (base::StringSplitter sp(filter_string, ','); sp.Next();) {
+        base::Optional<uint32_t> maybe = base::CStringToUInt32(sp.cur_token());
+        if (maybe) {
+          selected_tracks.insert(TrackId{maybe.value()});
+        }
+      }
+    }
+  }
+
+  StringPool::Id filter_id =
+      string_pool_->InternString(base::StringView(filter_string));
+  return AddLayoutColumn(*slice_table_, selected_tracks, filter_id);
+}
+
+// The problem we're trying to solve is this: given a number of tracks each of
+// which contain a number of 'stalactites' - depth 0 slices and all their
+// children - layout the stalactites to minimize vertical depth without
+// changing the horizontal (time) position. So given two tracks:
+// Track A:
+//     aaaaaaaaa       aaa
+//                      aa
+//                       a
+// Track B:
+//      bbb       bbb    bbb
+//       b         b      b
+// The result could be something like:
+//     aaaaaaaaa  bbb  aaa
+//                 b    aa
+//      bbb              a
+//       b
+//                       bbb
+//                        b
+// We do this by computing an additional column: layout_depth. layout_depth
+// tells us the vertical position of each slice in each stalactite.
+//
+// The algorithm works in three passes:
+// 1. For each stalactite find the 'bounding box' (start, end, & max depth)
+// 2. Considering each stalactite bounding box in start ts order pick a
+//    layout_depth for the root slice of stalactite to avoid collisions with
+//    all previous stalactite's we've considered.
+// 3. Go though each slice and give it a layout_depth by summing it's
+//    current depth and the root layout_depth of the stalactite it belongs to.
+//
+//
+std::unique_ptr<Table> ExperimentalSliceLayoutGenerator::AddLayoutColumn(
+    const Table& table,
+    const std::set<TrackId>& selected,
+    StringPool::Id filter_id) {
+  const auto& track_id_col =
+      *table.GetTypedColumnByName<tables::TrackTable::Id>("track_id");
+  const auto& depth_col = *table.GetTypedColumnByName<uint32_t>("depth");
+  const auto& ts_col = *table.GetTypedColumnByName<int64_t>("ts");
+  const auto& dur_col = *table.GetTypedColumnByName<int64_t>("dur");
+
+  std::map<TrackId, GroupInfo> groups;
+
+  // Step 1:
+  // Find the bounding box (start ts, end ts, and max depth) for each group
+  // TODO(lalitm): Update this to use iterator (will be slow after event table)
+  for (uint32_t i = 0; i < table.row_count(); ++i) {
+    TrackId track_id = track_id_col[i];
+    if (selected.count(track_id) == 0) {
+      continue;
+    }
+
+    uint32_t depth = depth_col[i];
+    int64_t start = ts_col[i];
+    int64_t dur = dur_col[i];
+    int64_t end = start + dur;
+
+    std::map<TrackId, GroupInfo>::iterator it;
+    bool inserted;
+    std::tie(it, inserted) = groups.emplace(
+        std::piecewise_construct, std::forward_as_tuple(track_id),
+        std::forward_as_tuple(start, end, depth + 1));
+    if (!inserted) {
+      it->second.max_height = std::max(it->second.max_height, depth + 1);
+      it->second.end = std::max(it->second.end, end);
+    }
+  }
+
+  // Step 2:
+  // Go though each group and choose a depth for the root slice.
+  // We keep track of those groups where the start time has passed but the
+  // end time has not in this vector:
+  std::vector<GroupInfo*> still_open;
+  for (auto& id_group : groups) {
+    int64_t start = id_group.second.start;
+    uint32_t max_height = id_group.second.max_height;
+
+    // Discard all 'closed' groups where that groups end_ts is < our start_ts:
+    {
+      auto it = still_open.begin();
+      while (it != still_open.end()) {
+        if ((*it)->end < start) {
+          it = still_open.erase(it);
+        } else {
+          ++it;
+        }
+      }
+    }
+
+    // Find a start layout depth for this group s.t. our start depth +
+    // our max depth will not intersect with the start depth + max depth for
+    // any of the open groups:
+    uint32_t layout_depth = 0;
+    bool done = false;
+    while (!done) {
+      done = true;
+      uint32_t start_depth = layout_depth;
+      uint32_t end_depth = layout_depth + max_height;
+      for (const auto& open : still_open) {
+        bool top = open->layout_depth <= start_depth &&
+                   start_depth < open->layout_depth + open->max_height;
+        bool bottom = open->layout_depth < end_depth &&
+                      end_depth <= open->layout_depth + open->max_height;
+        if (top || bottom) {
+          // This is extremely dumb, we can make a much better guess for what
+          // depth to try next but it is a little complicated to get right.
+          layout_depth++;
+          done = false;
+          break;
+        }
+      }
+    }
+
+    // Add this group to the open groups & re
+    still_open.push_back(&id_group.second);
+
+    // Set our root layout depth:
+    id_group.second.layout_depth = layout_depth;
+  }
+
+  // Step 3: Add the two new columns layout_depth and filter_track_ids:
+  std::unique_ptr<SparseVector<int64_t>> layout_depth_column(
+      new SparseVector<int64_t>());
+  std::unique_ptr<SparseVector<StringPool::Id>> filter_column(
+      new SparseVector<StringPool::Id>());
+
+  for (uint32_t i = 0; i < table.row_count(); ++i) {
+    TrackId track_id = track_id_col[i];
+    uint32_t depth = depth_col[i];
+    if (selected.count(track_id) == 0) {
+      // Don't care about depth for slices from non-selected tracks:
+      layout_depth_column->Append(0);
+      // We (ab)use this column to also filter out all the slices we don't care
+      // about by giving it a different value.
+      filter_column->Append(empty_string_id_);
+    } else {
+      // Each slice depth is it's current slice depth + root slice depth of the
+      // group:
+      layout_depth_column->Append(depth + groups.at(track_id).layout_depth);
+      // We must set this to the value we got in the constraint to ensure our
+      // rows are not filtered out:
+      filter_column->Append(filter_id);
+    }
+  }
+
+  return std::unique_ptr<Table>(new Table(
+      table
+          .ExtendWithColumn("layout_depth", std::move(layout_depth_column),
+                            TypedColumn<int64_t>::default_flags())
+          .ExtendWithColumn("filter_track_ids", std::move(filter_column),
+                            TypedColumn<StringPool::Id>::default_flags())));
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/dynamic/experimental_slice_layout_generator.h b/src/trace_processor/dynamic/experimental_slice_layout_generator.h
new file mode 100644
index 0000000..79f0f16
--- /dev/null
+++ b/src/trace_processor/dynamic/experimental_slice_layout_generator.h
@@ -0,0 +1,58 @@
+/*
+ * 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_DYNAMIC_EXPERIMENTAL_SLICE_LAYOUT_GENERATOR_H_
+#define SRC_TRACE_PROCESSOR_DYNAMIC_EXPERIMENTAL_SLICE_LAYOUT_GENERATOR_H_
+
+#include <set>
+
+#include "src/trace_processor/sqlite/db_sqlite_table.h"
+#include "src/trace_processor/storage/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class ExperimentalSliceLayoutGenerator
+    : public DbSqliteTable::DynamicTableGenerator {
+ public:
+  static constexpr uint32_t kFilterTrackIdsColumnIndex =
+      static_cast<uint32_t>(tables::SliceTable::ColumnIndex::arg_set_id) + 2;
+
+  ExperimentalSliceLayoutGenerator(StringPool* string_pool,
+                                   const tables::SliceTable* table);
+  virtual ~ExperimentalSliceLayoutGenerator() override;
+
+  Table::Schema CreateSchema() override;
+  std::string TableName() override;
+  uint32_t EstimateRowCount() override;
+  util::Status ValidateConstraints(const QueryConstraints&) override;
+  std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>&,
+                                      const std::vector<Order>&) override;
+
+ private:
+  std::unique_ptr<Table> AddLayoutColumn(const Table& table,
+                                         const std::set<TrackId>& selected,
+                                         StringPool::Id filter_id);
+
+  StringPool* string_pool_;
+  const tables::SliceTable* slice_table_;
+  const StringPool::Id empty_string_id_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_DYNAMIC_EXPERIMENTAL_SLICE_LAYOUT_GENERATOR_H_
diff --git a/src/trace_processor/dynamic/experimental_slice_layout_generator_unittest.cc b/src/trace_processor/dynamic/experimental_slice_layout_generator_unittest.cc
new file mode 100644
index 0000000..9b32f17
--- /dev/null
+++ b/src/trace_processor/dynamic/experimental_slice_layout_generator_unittest.cc
@@ -0,0 +1,192 @@
+/*
+ * 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/dynamic/experimental_slice_layout_generator.h"
+
+#include <algorithm>
+
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+constexpr uint32_t kColumn =
+    ExperimentalSliceLayoutGenerator::kFilterTrackIdsColumnIndex;
+
+std::string ToVis(const Table& table) {
+  const Column* layout_depth_column = table.GetColumnByName("layout_depth");
+  const Column* ts_column = table.GetColumnByName("ts");
+  const Column* dur_column = table.GetColumnByName("dur");
+  const Column* filter_track_ids_column =
+      table.GetColumnByName("filter_track_ids");
+
+  std::vector<std::string> lines;
+  for (uint32_t i = 0; i < table.row_count(); ++i) {
+    int64_t layout_depth = layout_depth_column->Get(i).long_value;
+    int64_t ts = ts_column->Get(i).long_value;
+    int64_t dur = dur_column->Get(i).long_value;
+    const char* filter_track_ids = filter_track_ids_column->Get(i).AsString();
+    if (std::string("") == filter_track_ids) {
+      continue;
+    }
+    for (int64_t j = 0; j < dur; ++j) {
+      size_t y = static_cast<size_t>(layout_depth);
+      size_t x = static_cast<size_t>(ts + j);
+      while (lines.size() <= y) {
+        lines.push_back("");
+      }
+      if (lines[y].size() <= x) {
+        lines[y].resize(x + 1, ' ');
+      }
+      lines[y][x] = '#';
+    }
+  }
+
+  std::string output = "";
+  output += "\n";
+  for (const std::string& line : lines) {
+    output += line;
+    output += "\n";
+  }
+  return output;
+}
+
+void ExpectOutput(const Table& table, const std::string& expected) {
+  const auto& actual = ToVis(table);
+  EXPECT_EQ(actual, expected)
+      << "Actual:" << actual << "\nExpected:" << expected;
+}
+
+tables::SliceTable::Row SliceRow(int64_t ts,
+                                 int64_t dur,
+                                 uint32_t depth,
+                                 uint32_t track_id) {
+  tables::SliceTable::Row row;
+  row.ts = ts;
+  row.dur = dur;
+  row.depth = depth;
+  row.track_id = tables::TrackTable::Id{track_id};
+  return row;
+}
+
+TEST(ExperimentalSliceLayoutGeneratorTest, SingleRow) {
+  StringPool pool;
+  tables::SliceTable slice_table(&pool, nullptr);
+
+  slice_table.Insert(SliceRow(1 /*ts*/, 5 /*dur*/, 0u /*depth*/, 1u /*id*/));
+
+  ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
+
+  std::unique_ptr<Table> table = gen.ComputeTable(
+      {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1")}}, {});
+  ExpectOutput(*table, R"(
+ #####
+)");
+}
+
+TEST(ExperimentalSliceLayoutGeneratorTest, MultipleRows) {
+  StringPool pool;
+  tables::SliceTable slice_table(&pool, nullptr);
+
+  slice_table.Insert(SliceRow(1 /*ts*/, 5 /*dur*/, 0u /*depth*/, 1u /*id*/));
+  slice_table.Insert(SliceRow(1 /*ts*/, 4 /*dur*/, 1u /*depth*/, 1u /*id*/));
+  slice_table.Insert(SliceRow(1 /*ts*/, 3 /*dur*/, 2u /*depth*/, 1u /*id*/));
+  slice_table.Insert(SliceRow(1 /*ts*/, 2 /*dur*/, 3u /*depth*/, 1u /*id*/));
+  slice_table.Insert(SliceRow(1 /*ts*/, 1 /*dur*/, 4u /*depth*/, 1u /*id*/));
+
+  ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
+
+  std::unique_ptr<Table> table = gen.ComputeTable(
+      {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1")}}, {});
+  ExpectOutput(*table, R"(
+ #####
+ ####
+ ###
+ ##
+ #
+)");
+}
+
+TEST(ExperimentalSliceLayoutGeneratorTest, MultipleTracks) {
+  StringPool pool;
+  tables::SliceTable slice_table(&pool, nullptr);
+
+  slice_table.Insert(SliceRow(0 /*ts*/, 4 /*dur*/, 0u /*depth*/, 1u /*id*/));
+  slice_table.Insert(SliceRow(0 /*ts*/, 2 /*dur*/, 1u /*depth*/, 1u /*id*/));
+  slice_table.Insert(SliceRow(3 /*ts*/, 4 /*dur*/, 0u /*depth*/, 2u /*id*/));
+  slice_table.Insert(SliceRow(3 /*ts*/, 2 /*dur*/, 1u /*depth*/, 2u /*id*/));
+
+  ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
+
+  std::unique_ptr<Table> table = gen.ComputeTable(
+      {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1,2")}}, {});
+  ExpectOutput(*table, R"(
+####
+##
+   ####
+   ##
+)");
+}
+
+TEST(ExperimentalSliceLayoutGeneratorTest, MultipleTracksWithGap) {
+  StringPool pool;
+  tables::SliceTable slice_table(&pool, nullptr);
+
+  slice_table.Insert(SliceRow(0 /*ts*/, 4 /*dur*/, 0u /*depth*/, 1u /*id*/));
+  slice_table.Insert(SliceRow(0 /*ts*/, 2 /*dur*/, 1u /*depth*/, 1u /*id*/));
+  slice_table.Insert(SliceRow(3 /*ts*/, 4 /*dur*/, 0u /*depth*/, 2u /*id*/));
+  slice_table.Insert(SliceRow(3 /*ts*/, 2 /*dur*/, 1u /*depth*/, 2u /*id*/));
+  slice_table.Insert(SliceRow(5 /*ts*/, 4 /*dur*/, 0u /*depth*/, 3u /*id*/));
+  slice_table.Insert(SliceRow(5 /*ts*/, 2 /*dur*/, 1u /*depth*/, 3u /*id*/));
+
+  ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
+
+  std::unique_ptr<Table> table = gen.ComputeTable(
+      {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1,2,3")}}, {});
+  ExpectOutput(*table, R"(
+#### ####
+##   ##
+   ####
+   ##
+)");
+}
+
+TEST(ExperimentalSliceLayoutGeneratorTest, FilterOutTracks) {
+  StringPool pool;
+  tables::SliceTable slice_table(&pool, nullptr);
+
+  slice_table.Insert(SliceRow(0 /*ts*/, 4 /*dur*/, 0u /*depth*/, 1u /*id*/));
+  slice_table.Insert(SliceRow(0 /*ts*/, 2 /*dur*/, 1u /*depth*/, 1u /*id*/));
+  slice_table.Insert(SliceRow(3 /*ts*/, 4 /*dur*/, 0u /*depth*/, 2u /*id*/));
+  slice_table.Insert(SliceRow(3 /*ts*/, 2 /*dur*/, 1u /*depth*/, 2u /*id*/));
+  // This slice should be ignored as it's not in the filter below:
+  slice_table.Insert(SliceRow(0 /*ts*/, 9 /*dur*/, 0u /*depth*/, 3u /*id*/));
+
+  ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
+  std::unique_ptr<Table> table = gen.ComputeTable(
+      {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1,2")}}, {});
+  ExpectOutput(*table, R"(
+####
+##
+   ####
+   ##
+)");
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/event_tracker_unittest.cc b/src/trace_processor/event_tracker_unittest.cc
deleted file mode 100644
index d8f41ed..0000000
--- a/src/trace_processor/event_tracker_unittest.cc
+++ /dev/null
@@ -1,140 +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/event_tracker.h"
-
-#include "perfetto/base/logging.h"
-#include "src/trace_processor/args_tracker.h"
-#include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
-#include "src/trace_processor/process_tracker.h"
-#include "src/trace_processor/track_tracker.h"
-#include "test/gtest_and_gmock.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace {
-
-using ::testing::_;
-using ::testing::InSequence;
-using ::testing::Invoke;
-
-class EventTrackerTest : public ::testing::Test {
- 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));
-    context.track_tracker.reset(new TrackTracker(&context));
-    sched_tracker = SchedEventTracker::GetOrCreate(&context);
-  }
-
- protected:
-  TraceProcessorContext context;
-  SchedEventTracker* sched_tracker;
-};
-
-TEST_F(EventTrackerTest, InsertSecondSched) {
-  uint32_t cpu = 3;
-  int64_t timestamp = 100;
-  uint32_t pid_1 = 2;
-  int64_t prev_state = 32;
-  static const char kCommProc1[] = "process1";
-  static const char kCommProc2[] = "process2";
-  uint32_t pid_2 = 4;
-  int32_t prio = 1024;
-
-  const auto& timestamps = context.storage->slices().start_ns();
-  sched_tracker->PushSchedSwitch(cpu, timestamp, pid_1, kCommProc2, prio,
-                                 prev_state, pid_2, kCommProc1, prio);
-  ASSERT_EQ(timestamps.size(), 1u);
-
-  sched_tracker->PushSchedSwitch(cpu, timestamp + 1, pid_2, kCommProc1, prio,
-                                 prev_state, pid_1, kCommProc2, prio);
-
-  ASSERT_EQ(timestamps.size(), 2ul);
-  ASSERT_EQ(timestamps[0], timestamp);
-  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);
-}
-
-TEST_F(EventTrackerTest, InsertThirdSched_SameThread) {
-  uint32_t cpu = 3;
-  int64_t timestamp = 100;
-  int64_t prev_state = 32;
-  static const char kCommProc1[] = "process1";
-  static const char kCommProc2[] = "process2";
-  int32_t prio = 1024;
-
-  const auto& timestamps = context.storage->slices().start_ns();
-  sched_tracker->PushSchedSwitch(cpu, timestamp, /*tid=*/4, kCommProc2, prio,
-                                 prev_state,
-                                 /*tid=*/2, kCommProc1, prio);
-  ASSERT_EQ(timestamps.size(), 1u);
-
-  sched_tracker->PushSchedSwitch(cpu, timestamp + 1, /*tid=*/2, kCommProc1,
-                                 prio, prev_state,
-                                 /*tid=*/4, kCommProc2, prio);
-  sched_tracker->PushSchedSwitch(cpu, timestamp + 11, /*tid=*/4, kCommProc2,
-                                 prio, prev_state,
-                                 /*tid=*/2, kCommProc1, prio);
-  sched_tracker->PushSchedSwitch(cpu, timestamp + 31, /*tid=*/2, kCommProc1,
-                                 prio, prev_state,
-                                 /*tid=*/4, kCommProc2, prio);
-
-  ASSERT_EQ(timestamps.size(), 4ul);
-  ASSERT_EQ(timestamps[0], timestamp);
-  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);
-  ASSERT_EQ(context.storage->slices().utids().at(0),
-            context.storage->slices().utids().at(2));
-}
-
-TEST_F(EventTrackerTest, CounterDuration) {
-  uint32_t cpu = 3;
-  int64_t timestamp = 100;
-  StringId name_id = kNullStringId;
-
-  TrackId track = context.track_tracker->InternCpuCounterTrack(name_id, cpu);
-  context.event_tracker->PushCounter(timestamp, 1000, track);
-  context.event_tracker->PushCounter(timestamp + 1, 4000, track);
-  context.event_tracker->PushCounter(timestamp + 3, 5000, track);
-  context.event_tracker->PushCounter(timestamp + 9, 1000, track);
-
-  ASSERT_EQ(context.storage->counter_track_table().row_count(), 1ul);
-
-  ASSERT_EQ(context.storage->counter_table().row_count(), 4ul);
-  ASSERT_EQ(context.storage->counter_table().ts()[0], timestamp);
-  ASSERT_DOUBLE_EQ(context.storage->counter_table().value()[0], 1000);
-
-  ASSERT_EQ(context.storage->counter_table().ts()[1], timestamp + 1);
-  ASSERT_DOUBLE_EQ(context.storage->counter_table().value()[1], 4000);
-
-  ASSERT_EQ(context.storage->counter_table().ts()[2], timestamp + 3);
-  ASSERT_DOUBLE_EQ(context.storage->counter_table().value()[2], 5000);
-}
-
-}  // namespace
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/export_json.cc b/src/trace_processor/export_json.cc
index 1e78477..c99c15b 100644
--- a/src/trace_processor/export_json.cc
+++ b/src/trace_processor/export_json.cc
@@ -14,29 +14,31 @@
  * limitations under the License.
  */
 
-// For bazel build.
-#include "perfetto/base/build_config.h"
-#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
-
 #include "perfetto/ext/trace_processor/export_json.h"
 #include "src/trace_processor/export_json.h"
 
 #include <inttypes.h>
-#include <json/reader.h>
-#include <json/value.h>
-#include <json/writer.h>
 #include <stdio.h>
 
 #include <algorithm>
+#include <cmath>
 #include <cstring>
 #include <deque>
 #include <limits>
 
+#include "perfetto/base/build_config.h"
 #include "perfetto/ext/base/string_splitter.h"
-#include "src/trace_processor/metadata.h"
-#include "src/trace_processor/trace_processor_context.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "src/trace_processor/importers/json/json_utils.h"
+#include "src/trace_processor/storage/metadata.h"
+#include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/trace_processor_storage_impl.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
+#include <json/reader.h>
+#include <json/writer.h>
+#endif
 
 namespace perfetto {
 namespace trace_processor {
@@ -44,6 +46,24 @@
 
 namespace {
 
+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_;
+};
+
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
 using IndexMap = perfetto::trace_processor::TraceStorage::Stats::IndexMap;
 
 const char kLegacyEventArgsKey[] = "legacy_event";
@@ -103,23 +123,6 @@
   }
 }
 
-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 JsonExporter {
  public:
   JsonExporter(const TraceStorage* storage,
@@ -525,7 +528,7 @@
 
       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]);
+        const char* key = arg_table.key().GetString(i).c_str();
         Variadic value = storage_->GetArgValue(i);
         AppendArg(set_id, key, VariadicToJson(value));
       }
@@ -592,7 +595,10 @@
         } else {  // A list item
           target = &(*target)[key_part.substr(0, bracketpos)];
           while (bracketpos != key_part.npos) {
-            std::string index =
+            // 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())) {
@@ -601,7 +607,13 @@
                             args_sets_[set_id].toStyledString().c_str());
               return;
             }
-            target = &(*target)[stoi(index)];
+            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);
           }
         }
@@ -748,17 +760,19 @@
       // 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];
+      TrackId track_id = slices.track_id()[i];
 
       const auto& track_table = storage_->track_table();
 
-      uint32_t track_row = *track_table.id().IndexOf(TrackId{track_id});
+      uint32_t track_row = *track_table.id().IndexOf(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();
@@ -801,7 +815,7 @@
 
       auto opt_thread_track_row = thread_track.id().IndexOf(TrackId{track_id});
 
-      if (opt_thread_track_row) {
+      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);
@@ -842,7 +856,7 @@
           }
         }
         writer_.WriteCommonEvent(event);
-      } else if (!legacy_chrome_track ||
+      } 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});
@@ -879,20 +893,32 @@
             event["id"] = PrintUint64(source_id);
           }
         } else {
-          if (opt_process_row) {
+          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.value);
+          } else if (opt_process_row) {
             uint32_t upid = process_track.upid()[*opt_process_row];
-            event["id2"]["local"] = PrintUint64(track_id);
             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.value);
           } else {
+            if (legacy_utid) {
+              auto pid_and_tid = UtidToPidAndTid(*legacy_utid);
+              event["pid"] = Json::Int(pid_and_tid.first);
+              event["tid"] = Json::Int(pid_and_tid.second);
+            }
+
             // 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);
+            event["id"] = PrintUint64(track_id.value);
           }
         }
 
@@ -930,24 +956,31 @@
         }
       } else {
         // Global or process-scoped instant event.
-        PERFETTO_DCHECK(legacy_chrome_track);
-        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];
-          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";
+        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 {
-          event["s"] = "g";
+          // 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);
         }
-        writer_.WriteCommonEvent(event);
       }
     }
     return util::OkStatus();
@@ -1080,7 +1113,7 @@
       event["tid"] = Json::Int(pid_and_tid.second);
 
       event["ph"] = "n";
-      event["cat"] = "disabled_by_default-cpu_profiler";
+      event["cat"] = "disabled-by-default-cpu_profiler";
       event["name"] = "StackCpuSampling";
       event["s"] = "t";
 
@@ -1096,45 +1129,43 @@
       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();
+      const auto& mappings = storage_->stack_profile_mapping_table();
 
-        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]);
+      std::vector<std::string> callstack;
+      base::Optional<CallsiteId> opt_callsite_id = samples.callsite_id()[i];
 
-        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]);
+      while (opt_callsite_id) {
+        CallsiteId callsite_id = *opt_callsite_id;
+        uint32_t callsite_row = *callsites.id().IndexOf(callsite_id);
+
+        FrameId frame_id = callsites.frame_id()[callsite_row];
+        uint32_t frame_row = *frames.id().IndexOf(frame_id);
+
+        MappingId mapping_id = frames.mapping()[frame_row];
+        uint32_t mapping_row = *mappings.id().IndexOf(mapping_id);
 
         NullTermStringView symbol_name;
-        auto opt_symbol_set_id = frames.symbol_set_id()[frame_id];
+        auto opt_symbol_set_id = frames.symbol_set_id()[frame_row];
         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]));
+        snprintf(frame_entry, sizeof(frame_entry), "%s - %s [%s]\n",
+                 (symbol_name.empty()
+                      ? PrintUint64(
+                            static_cast<uint64_t>(frames.rel_pc()[frame_row]))
+                            .c_str()
+                      : symbol_name.c_str()),
+                 GetNonNullString(storage_, mappings.name()[mapping_row]),
+                 GetNonNullString(storage_, mappings.build_id()[mapping_row]));
 
         callstack.emplace_back(frame_entry);
 
-        maybe_callsite_id = callsites.parent_id()[callsite_id];
+        opt_callsite_id = callsites.parent_id()[callsite_row];
       }
 
       std::string merged_callstack;
@@ -1144,6 +1175,7 @@
       }
 
       event["args"]["frames"] = merged_callstack;
+      event["args"]["process_priority"] = samples.process_priority()[i];
 
       // TODO(oysteine): Used for backwards compatibility with the memlog
       // pipeline, should remove once we've switched to looking directly at the
@@ -1176,17 +1208,15 @@
       switch (static_cast<size_t>(key)) {
         case metadata::benchmark_description:
           writer_.AppendTelemetryMetadataString(
-              "benchmarkDescriptions",
-              GetNonNullString(storage_, str_values[pos]));
+              "benchmarkDescriptions", str_values.GetString(pos).c_str());
           break;
 
         case metadata::benchmark_name:
           writer_.AppendTelemetryMetadataString(
-              "benchmarks", GetNonNullString(storage_, str_values[pos]));
+              "benchmarks", str_values.GetString(pos).c_str());
           break;
 
         case metadata::benchmark_start_time_us:
-
           writer_.SetTelemetryMetadataTimestamp("benchmarkStart",
                                                 *int_values[pos]);
           break;
@@ -1197,12 +1227,12 @@
 
         case metadata::benchmark_label:
           writer_.AppendTelemetryMetadataString(
-              "labels", GetNonNullString(storage_, str_values[pos]));
+              "labels", str_values.GetString(pos).c_str());
           break;
 
         case metadata::benchmark_story_name:
           writer_.AppendTelemetryMetadataString(
-              "stories", GetNonNullString(storage_, str_values[pos]));
+              "stories", str_values.GetString(pos).c_str());
           break;
 
         case metadata::benchmark_story_run_index:
@@ -1216,7 +1246,7 @@
 
         case metadata::benchmark_story_tags:  // repeated
           writer_.AppendTelemetryMetadataString(
-              "storyTags", GetNonNullString(storage_, str_values[pos]));
+              "storyTags", str_values.GetString(pos).c_str());
           break;
 
         default:
@@ -1297,6 +1327,8 @@
       exported_pids_and_tids_to_utids_;
 };
 
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
+
 }  // namespace
 
 OutputWriter::OutputWriter() = default;
@@ -1307,9 +1339,18 @@
                         ArgumentFilterPredicate argument_filter,
                         MetadataFilterPredicate metadata_filter,
                         LabelFilterPredicate label_filter) {
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
   JsonExporter exporter(storage, output, std::move(argument_filter),
                         std::move(metadata_filter), std::move(label_filter));
   return exporter.Export();
+#else
+  perfetto::base::ignore_result(storage);
+  perfetto::base::ignore_result(output);
+  perfetto::base::ignore_result(argument_filter);
+  perfetto::base::ignore_result(metadata_filter);
+  perfetto::base::ignore_result(label_filter);
+  return util::ErrStatus("JSON support is not compiled in this build");
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
 }
 
 util::Status ExportJson(TraceProcessorStorage* tp,
@@ -1333,4 +1374,3 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
diff --git a/src/trace_processor/export_json.h b/src/trace_processor/export_json.h
index 5d2ea54..47726bf 100644
--- a/src/trace_processor/export_json.h
+++ b/src/trace_processor/export_json.h
@@ -21,7 +21,7 @@
 
 #include "perfetto/ext/trace_processor/export_json.h"
 #include "perfetto/trace_processor/status.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/trace_storage.h"
 
 namespace perfetto {
 namespace trace_processor {
diff --git a/src/trace_processor/export_json_unittest.cc b/src/trace_processor/export_json_unittest.cc
index 6d149e6..d0e8ad5 100644
--- a/src/trace_processor/export_json_unittest.cc
+++ b/src/trace_processor/export_json_unittest.cc
@@ -25,11 +25,11 @@
 #include <json/value.h>
 
 #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"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/importers/proto/metadata_tracker.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 #include "test/gtest_and_gmock.h"
 
@@ -124,7 +124,7 @@
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
   context_.storage->mutable_slice_table()->Insert(
-      {kTimestamp, kDuration, track.value, cat_id, name_id, 0, 0, 0});
+      {kTimestamp, kDuration, track, cat_id, name_id, 0, 0, 0});
   context_.storage->mutable_thread_slices()->AddThreadSlice(
       0, kThreadTimestamp, kThreadDuration, kThreadInstructionCount,
       kThreadInstructionDelta);
@@ -170,7 +170,7 @@
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
   context_.storage->mutable_slice_table()->Insert(
-      {kTimestamp, kDuration, track.value, cat_id, name_id, 0, 0, 0});
+      {kTimestamp, kDuration, track, cat_id, name_id, 0, 0, 0});
   context_.storage->mutable_thread_slices()->AddThreadSlice(
       0, kThreadTimestamp, kThreadDuration, kThreadInstructionCount,
       kThreadInstructionDelta);
@@ -233,7 +233,7 @@
   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});
+      {0, 0, track, cat_id, name_id, 0, 0, 0});
 
   base::TempFile temp_file = base::TempFile::Create();
   FILE* output = fopen(temp_file.path().c_str(), "w+");
@@ -373,8 +373,10 @@
 
   TraceStorage* storage = context_.storage.get();
 
-  RawId id = storage->mutable_raw_table()->Insert(
-      {0, storage->InternString("chrome_event.metadata"), 0, 0});
+  RawId id =
+      storage->mutable_raw_table()
+          ->Insert({0, storage->InternString("chrome_event.metadata"), 0, 0})
+          .id;
 
   StringId name1_id = storage->InternString(base::StringView(kName1));
   StringId name2_id = storage->InternString(base::StringView(kName2));
@@ -410,7 +412,7 @@
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
   context_.storage->mutable_slice_table()->Insert(
-      {0, 0, track.value, cat_id, name_id, 0, 0, 0});
+      {0, 0, track, cat_id, name_id, 0, 0, 0});
 
   StringId arg_key_id = context_.storage->InternString(
       base::StringView("task.posted_from.file_name"));
@@ -453,8 +455,9 @@
   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});
+  SliceId id = storage->mutable_slice_table()
+                   ->Insert({0, 0, track, cat_id, name_id, 0, 0, 0})
+                   .id;
   auto inserter = context_.args_tracker->AddArgsTo(id);
 
   auto add_arg = [&](const char* key, Variadic value) {
@@ -501,7 +504,7 @@
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
   context_.storage->mutable_slice_table()->Insert(
-      {0, 0, track.value, cat_id, name_id, 0, 0, 0});
+      {0, 0, track, cat_id, name_id, 0, 0, 0});
 
   StringId arg_flat_key_id = context_.storage->InternString(
       base::StringView("debug.draw_duration_ms"));
@@ -549,7 +552,7 @@
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
   context_.storage->mutable_slice_table()->Insert(
-      {0, 0, track.value, cat_id, name_id, 0, 0, 0});
+      {0, 0, track, cat_id, name_id, 0, 0, 0});
 
   StringId arg_key0_id =
       context_.storage->InternString(base::StringView("arg0"));
@@ -593,7 +596,7 @@
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
   context_.storage->mutable_slice_table()->Insert(
-      {0, 0, track.value, cat_id, name_id, 0, 0, 0});
+      {0, 0, track, cat_id, name_id, 0, 0, 0});
 
   StringId arg_flat_key_id =
       context_.storage->InternString(base::StringView("a.b"));
@@ -640,7 +643,7 @@
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
   context_.storage->mutable_slice_table()->Insert(
-      {0, 0, track.value, cat_id, name_id, 0, 0, 0});
+      {0, 0, track, cat_id, name_id, 0, 0, 0});
 
   StringId arg_flat_key_id =
       context_.storage->InternString(base::StringView("a"));
@@ -687,7 +690,7 @@
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
   context_.storage->mutable_slice_table()->Insert(
-      {0, 0, track.value, cat_id, name_id, 0, 0, 0});
+      {0, 0, track, cat_id, name_id, 0, 0, 0});
 
   StringId arg_key_id = context_.storage->InternString(base::StringView("a"));
   StringId arg_value_id =
@@ -716,16 +719,32 @@
 
 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.
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
   context_.storage->mutable_slice_table()->Insert(
-      {kTimestamp, 0, track.value, cat_id, name_id, 0, 0, 0});
+      {kTimestamp, 0, track, 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, 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, cat_id, name_id, 0, 0, 0});
 
   base::TempFile temp_file = base::TempFile::Create();
   FILE* output = fopen(temp_file.path().c_str(), "w+");
@@ -734,7 +753,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");
@@ -742,6 +761,20 @@
   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) {
@@ -756,7 +789,7 @@
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
   context_.storage->mutable_slice_table()->Insert(
-      {kTimestamp, 0, track.value, cat_id, name_id, 0, 0, 0});
+      {kTimestamp, 0, track, cat_id, name_id, 0, 0, 0});
 
   base::TempFile temp_file = base::TempFile::Create();
   FILE* output = fopen(temp_file.path().c_str(), "w+");
@@ -823,15 +856,15 @@
       context_.storage->InternString(base::StringView("name2b"));
 
   context_.storage->mutable_slice_table()->Insert(
-      {10000, 0, track1a.value, cat_id, name1a_id, 0, 0, 0});
+      {10000, 0, track1a, cat_id, name1a_id, 0, 0, 0});
   context_.storage->mutable_slice_table()->Insert(
-      {20000, 1000, track1b.value, cat_id, name1b_id, 0, 0, 0});
+      {20000, 1000, track1b, cat_id, name1b_id, 0, 0, 0});
   context_.storage->mutable_slice_table()->Insert(
-      {30000, 0, track1c.value, cat_id, name1c_id, 0, 0, 0});
+      {30000, 0, track1c, cat_id, name1c_id, 0, 0, 0});
   context_.storage->mutable_slice_table()->Insert(
-      {40000, 0, track2a.value, cat_id, name2a_id, 0, 0, 0});
+      {40000, 0, track2a, cat_id, name2a_id, 0, 0, 0});
   context_.storage->mutable_slice_table()->Insert(
-      {50000, 1000, track2b.value, cat_id, name2b_id, 0, 0, 0});
+      {50000, 1000, track2b, cat_id, name2b_id, 0, 0, 0});
 
   base::TempFile temp_file = base::TempFile::Create();
   FILE* output = fopen(temp_file.path().c_str(), "w+");
@@ -913,7 +946,7 @@
   context_.args_tracker->Flush();  // Flush track args.
 
   context_.storage->mutable_slice_table()->Insert(
-      {kTimestamp, kDuration, track.value, cat_id, name_id, 0, 0, 0});
+      {kTimestamp, kDuration, track, cat_id, name_id, 0, 0, 0});
   StringId arg_key_id =
       context_.storage->InternString(base::StringView(kArgName));
   GlobalArgsTracker::Arg arg;
@@ -925,11 +958,11 @@
 
   // 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});
+      {kTimestamp, kDuration, track, 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});
+      {kTimestamp3, kDuration3, track2, cat_id, name3_id, 0, 0, 0});
 
   base::TempFile temp_file = base::TempFile::Create();
   FILE* output = fopen(temp_file.path().c_str(), "w+");
@@ -1034,10 +1067,11 @@
       /*source_scope=*/kNullStringId);
   context_.args_tracker->Flush();  // Flush track args.
 
-  auto slice_id = context_.storage->mutable_slice_table()->Insert(
-      {kTimestamp, kDuration, track.value, cat_id, name_id, 0, 0, 0});
+  auto* slices = context_.storage->mutable_slice_table();
+  auto id_and_row =
+      slices->Insert({kTimestamp, kDuration, track, cat_id, name_id, 0, 0, 0});
   context_.storage->mutable_virtual_track_slices()->AddVirtualTrackSlice(
-      slice_id.value, kThreadTimestamp, kThreadDuration, 0, 0);
+      id_and_row.id.value, kThreadTimestamp, kThreadDuration, 0, 0);
 
   base::TempFile temp_file = base::TempFile::Create();
   FILE* output = fopen(temp_file.path().c_str(), "w+");
@@ -1089,8 +1123,10 @@
       /*source_scope=*/kNullStringId);
   context_.args_tracker->Flush();  // Flush track args.
 
-  auto slice_id = context_.storage->mutable_slice_table()->Insert(
-      {kTimestamp, kDuration, track.value, cat_id, name_id, 0, 0, 0});
+  auto slice_id =
+      context_.storage->mutable_slice_table()
+          ->Insert({kTimestamp, kDuration, track, cat_id, name_id, 0, 0, 0})
+          .id;
   context_.storage->mutable_virtual_track_slices()->AddVirtualTrackSlice(
       slice_id.value, kThreadTimestamp, kThreadDuration, 0, 0);
 
@@ -1133,7 +1169,7 @@
   context_.args_tracker->Flush();  // Flush track args.
 
   context_.storage->mutable_slice_table()->Insert(
-      {kTimestamp, 0, track.value, cat_id, name_id, 0, 0, 0});
+      {kTimestamp, 0, track, cat_id, name_id, 0, 0, 0});
   StringId arg_key_id =
       context_.storage->InternString(base::StringView("arg_name"));
   GlobalArgsTracker::Arg arg;
@@ -1187,10 +1223,10 @@
   UniquePid upid = context_.process_tracker->GetOrCreateProcess(kProcessID);
   context_.storage->mutable_thread_table()->mutable_upid()->Set(utid, upid);
 
-  RawId id = storage->mutable_raw_table()->Insert(
+  auto id_and_row = storage->mutable_raw_table()->Insert(
       {kTimestamp, storage->InternString("track_event.legacy_event"), /*cpu=*/0,
        utid});
-  auto inserter = context_.args_tracker->AddArgsTo(id);
+  auto inserter = context_.args_tracker->AddArgsTo(id_and_row.id);
 
   auto add_arg = [&](const char* key, Variadic value) {
     StringId key_id = storage->InternString(key);
@@ -1262,24 +1298,25 @@
   const char* kLegacyJsonData2 = "er\": 1},{\"user\": 2}";
 
   TraceStorage* storage = context_.storage.get();
+  auto* raw = storage->mutable_raw_table();
 
-  RawId id = storage->mutable_raw_table()->Insert(
+  auto id_and_row = raw->Insert(
       {0, storage->InternString("chrome_event.legacy_system_trace"), 0, 0});
-  auto inserter = context_.args_tracker->AddArgsTo(id);
+  auto inserter = context_.args_tracker->AddArgsTo(id_and_row.id);
 
   StringId data_id = storage->InternString("data");
   StringId ftrace_data_id = storage->InternString(kLegacyFtraceData);
   inserter.AddArg(data_id, Variadic::String(ftrace_data_id));
 
-  id = storage->mutable_raw_table()->Insert(
+  id_and_row = raw->Insert(
       {0, storage->InternString("chrome_event.legacy_user_trace"), 0, 0});
-  inserter = context_.args_tracker->AddArgsTo(id);
+  inserter = context_.args_tracker->AddArgsTo(id_and_row.id);
   StringId json_data1_id = storage->InternString(kLegacyJsonData1);
   inserter.AddArg(data_id, Variadic::String(json_data1_id));
 
-  id = storage->mutable_raw_table()->Insert(
+  id_and_row = raw->Insert(
       {0, storage->InternString("chrome_event.legacy_user_trace"), 0, 0});
-  inserter = context_.args_tracker->AddArgsTo(id);
+  inserter = context_.args_tracker->AddArgsTo(id_and_row.id);
   StringId json_data2_id = storage->InternString(kLegacyJsonData2);
   inserter.AddArg(data_id, Variadic::String(json_data2_id));
 
@@ -1303,6 +1340,7 @@
   const uint32_t kProcessID = 100;
   const uint32_t kThreadID = 200;
   const int64_t kTimestamp = 10000000;
+  const int32_t kProcessPriority = 42;
 
   TraceStorage* storage = context_.storage.get();
 
@@ -1310,49 +1348,46 @@
   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,
-       storage->InternString("foo_module_name")});
+  auto* mappings = storage->mutable_stack_profile_mapping_table();
+  auto* frames = storage->mutable_stack_profile_frame_table();
+  auto* callsites = storage->mutable_stack_profile_callsite_table();
 
-  auto module_id_2 = storage->mutable_stack_profile_mapping_table()->Insert(
-      {storage->InternString("bar_module_id"), 0, 0, 0, 0, 0,
-       storage->InternString("bar_module_name")});
+  auto module_1 =
+      mappings->Insert({storage->InternString("foo_module_id"), 0, 0, 0, 0, 0,
+                        storage->InternString("foo_module_name")});
+
+  auto module_2 =
+      mappings->Insert({storage->InternString("bar_module_id"), 0, 0, 0, 0, 0,
+                        storage->InternString("bar_module_name")});
 
   // TODO(140860736): Once we support null values for
   // stack_profile_frame.symbol_set_id remove this hack
   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=*/kNullStringId, module_id_1.value, 0x42});
-  uint32_t frame_row_1 = *frames->id().IndexOf(frame_id_1);
+  auto frame_1 = frames->Insert({/*name_id=*/kNullStringId, module_1.id, 0x42});
 
   uint32_t symbol_set_id = storage->symbol_table().row_count();
   storage->mutable_symbol_table()->Insert(
       {symbol_set_id, storage->InternString("foo_func"),
        storage->InternString("foo_file"), 66});
-  frames->mutable_symbol_set_id()->Set(frame_row_1, symbol_set_id);
+  frames->mutable_symbol_set_id()->Set(frame_1.row, symbol_set_id);
 
-  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);
+  auto frame_2 =
+      frames->Insert({/*name_id=*/kNullStringId, module_2.id, 0x4242});
 
   symbol_set_id = storage->symbol_table().row_count();
   storage->mutable_symbol_table()->Insert(
       {symbol_set_id, storage->InternString("bar_func"),
        storage->InternString("bar_file"), 77});
-  frames->mutable_symbol_set_id()->Set(frame_row_2, symbol_set_id);
+  frames->mutable_symbol_set_id()->Set(frame_2.row, symbol_set_id);
 
-  auto frame_callsite_id_1 =
-      storage->mutable_stack_profile_callsite_table()->Insert(
-          {0, -1, frame_id_1.value});
+  auto frame_callsite_1 = callsites->Insert({0, base::nullopt, frame_1.id});
 
-  auto frame_callsite_id_2 =
-      storage->mutable_stack_profile_callsite_table()->Insert(
-          {1, frame_callsite_id_1.value, frame_id_2.value});
+  auto frame_callsite_2 =
+      callsites->Insert({1, frame_callsite_1.id, frame_2.id});
 
   storage->mutable_cpu_profile_stack_sample_table()->Insert(
-      {kTimestamp, frame_callsite_id_2.value, utid});
+      {kTimestamp, frame_callsite_2.id, utid, kProcessPriority});
 
   base::TempFile temp_file = base::TempFile::Create();
   FILE* output = fopen(temp_file.path().c_str(), "w+");
@@ -1368,12 +1403,13 @@
   EXPECT_EQ(event["id"].asString(), "0x1");
   EXPECT_EQ(event["ts"].asInt64(), kTimestamp / 1000);
   EXPECT_EQ(event["tid"].asInt(), static_cast<int>(kThreadID));
-  EXPECT_EQ(event["cat"].asString(), "disabled_by_default-cpu_profiler");
+  EXPECT_EQ(event["cat"].asString(), "disabled-by-default-cpu_profiler");
   EXPECT_EQ(event["name"].asString(), "StackCpuSampling");
   EXPECT_EQ(event["s"].asString(), "t");
   EXPECT_EQ(event["args"]["frames"].asString(),
             "foo_func - foo_module_name [foo_module_id]\nbar_func - "
             "bar_module_name [bar_module_id]\n");
+  EXPECT_EQ(event["args"]["process_priority"].asInt(), kProcessPriority);
 }
 
 TEST_F(ExportJsonTest, ArgumentFilter) {
@@ -1390,11 +1426,11 @@
   StringId arg2_id = context_.storage->InternString(base::StringView("arg2"));
   StringId val_id = context_.storage->InternString(base::StringView("val"));
 
+  auto* slices = context_.storage->mutable_slice_table();
   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_inserters.emplace_back(context_.args_tracker->AddArgsTo(slice_id));
+    auto id = slices->Insert({0, 0, track, cat_id, name_ids[i], 0, 0, 0}).id;
+    slice_inserters.emplace_back(context_.args_tracker->AddArgsTo(id));
   }
 
   for (auto& inserter : slice_inserters) {
@@ -1453,8 +1489,9 @@
 
   TraceStorage* storage = context_.storage.get();
 
-  RawId id = storage->mutable_raw_table()->Insert(
-      {0, storage->InternString("chrome_event.metadata"), 0, 0});
+  auto* raw = storage->mutable_raw_table();
+  RawId id =
+      raw->Insert({0, storage->InternString("chrome_event.metadata"), 0, 0}).id;
 
   StringId name1_id = storage->InternString(base::StringView(kName1));
   StringId name2_id = storage->InternString(base::StringView(kName2));
@@ -1494,9 +1531,9 @@
   StringId name_id = context_.storage->InternString(base::StringView(kName));
 
   context_.storage->mutable_slice_table()->Insert(
-      {kTimestamp1, kDuration, track.value, cat_id, name_id, 0, 0, 0});
+      {kTimestamp1, kDuration, track, cat_id, name_id, 0, 0, 0});
   context_.storage->mutable_slice_table()->Insert(
-      {kTimestamp2, kDuration, track.value, cat_id, name_id, 0, 0, 0});
+      {kTimestamp2, kDuration, track, cat_id, name_id, 0, 0, 0});
 
   auto label_filter = [](const char* label_name) {
     return strcmp(label_name, "traceEvents") == 0;
diff --git a/src/trace_processor/filtered_row_index.cc b/src/trace_processor/filtered_row_index.cc
deleted file mode 100644
index 376c021..0000000
--- a/src/trace_processor/filtered_row_index.cc
+++ /dev/null
@@ -1,152 +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/filtered_row_index.h"
-
-#include <iterator>
-#include <stddef.h>
-#include <numeric>
-
-namespace perfetto {
-namespace trace_processor {
-
-FilteredRowIndex::FilteredRowIndex(uint32_t start_row, uint32_t end_row)
-    : mode_(Mode::kAllRows), start_row_(start_row), end_row_(end_row) {}
-
-void FilteredRowIndex::IntersectRows(std::vector<uint32_t> rows) {
-  PERFETTO_DCHECK(error_.empty());
-
-  // Sort the rows so all branches below make sense.
-  std::sort(rows.begin(), rows.end());
-
-  if (mode_ == kAllRows) {
-    mode_ = Mode::kRowVector;
-    // Yes you're reading this code correctly. We use lower_bound in both cases.
-    // Yes this is very intentional and has to do with |end_row_| already being
-    // one greater than the value we are searching for so we need the first
-    // iterator which is *geq* the |end_row_|.
-    auto begin = std::lower_bound(rows.begin(), rows.end(), start_row_);
-    auto end = std::lower_bound(begin, rows.end(), end_row_);
-    rows_.insert(rows_.end(), begin, end);
-    return;
-  } else if (mode_ == kRowVector) {
-    std::vector<uint32_t> intersected;
-    std::set_intersection(rows_.begin(), rows_.end(), rows.begin(), rows.end(),
-                          std::back_inserter(intersected));
-    rows_ = std::move(intersected);
-    return;
-  }
-
-  // Initialise start to the beginning of the vector.
-  auto start = row_filter_.begin();
-
-  // Skip directly to the rows in range of start and end.
-  size_t i = 0;
-  for (; i < rows.size() && rows[i] < start_row_; i++) {
-  }
-  for (; i < rows.size() && rows[i] < end_row_; i++) {
-    // Unset all bits between the start iterator and the iterator pointing
-    // to the current row. That is, this loop sets all elements not pointed
-    // to by rows to false. It does not touch the rows themselves which
-    // means if they were already false (i.e. not returned) then they won't
-    // be returned now and if they were true (i.e. returned) they will still
-    // be returned.
-    auto row = rows[i];
-    auto end = row_filter_.begin() + static_cast<ptrdiff_t>(row - start_row_);
-    std::fill(start, end, false);
-    start = end + 1;
-  }
-  std::fill(start, row_filter_.end(), false);
-}
-
-std::vector<uint32_t> FilteredRowIndex::ToRowVector() {
-  PERFETTO_DCHECK(error_.empty());
-
-  switch (mode_) {
-    case Mode::kAllRows:
-      mode_ = Mode::kRowVector;
-      rows_.resize(end_row_ - start_row_);
-      std::iota(rows_.begin(), rows_.end(), start_row_);
-      break;
-    case Mode::kBitVector:
-      ConvertBitVectorToRowVector();
-      break;
-    case Mode::kRowVector:
-      // Nothing to do.
-      break;
-  }
-  return TakeRowVector();
-}
-
-void FilteredRowIndex::ConvertBitVectorToRowVector() {
-  PERFETTO_DCHECK(error_.empty());
-
-  mode_ = Mode::kRowVector;
-
-  auto b = row_filter_.begin();
-  auto e = row_filter_.end();
-  using std::find;
-  for (auto it = find(b, e, true); it != e; it = find(it + 1, e, true)) {
-    auto filter_idx = static_cast<uint32_t>(std::distance(b, it));
-    rows_.emplace_back(filter_idx + start_row_);
-  }
-  row_filter_.clear();
-}
-
-std::unique_ptr<RowIterator> FilteredRowIndex::ToRowIterator(bool desc) {
-  PERFETTO_DCHECK(error_.empty());
-
-  switch (mode_) {
-    case Mode::kAllRows:
-      return std::unique_ptr<RangeRowIterator>(
-          new RangeRowIterator(start_row_, end_row_, desc));
-    case Mode::kBitVector: {
-      return std::unique_ptr<RangeRowIterator>(
-          new RangeRowIterator(start_row_, desc, TakeBitVector()));
-    }
-    case Mode::kRowVector: {
-      auto vector = TakeRowVector();
-      if (desc)
-        std::reverse(vector.begin(), vector.end());
-      return std::unique_ptr<VectorRowIterator>(
-          new VectorRowIterator(std::move(vector)));
-    }
-  }
-  PERFETTO_FATAL("For GCC");
-}
-
-std::vector<uint32_t> FilteredRowIndex::TakeRowVector() {
-  PERFETTO_DCHECK(error_.empty());
-
-  PERFETTO_DCHECK(mode_ == Mode::kRowVector);
-  auto vector = std::move(rows_);
-  rows_.clear();
-  mode_ = Mode::kAllRows;
-  return vector;
-}
-
-std::vector<bool> FilteredRowIndex::TakeBitVector() {
-  PERFETTO_DCHECK(error_.empty());
-
-  PERFETTO_DCHECK(mode_ == Mode::kBitVector);
-  auto filter = std::move(row_filter_);
-  row_filter_.clear();
-  mode_ = Mode::kAllRows;
-  return filter;
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/filtered_row_index.h b/src/trace_processor/filtered_row_index.h
deleted file mode 100644
index c17fc56..0000000
--- a/src/trace_processor/filtered_row_index.h
+++ /dev/null
@@ -1,158 +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_FILTERED_ROW_INDEX_H_
-#define SRC_TRACE_PROCESSOR_FILTERED_ROW_INDEX_H_
-
-#include <stdint.h>
-#include <algorithm>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "perfetto/base/logging.h"
-#include "src/trace_processor/row_iterators.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-// Storage for information about the rows to be returned by a filter operation.
-//
-// There are two users of FilteredRowIndex:
-// 1. The filter classes which get told to filter all their rows on a certain
-// constraint, returning their result in an instance of this class. They will
-// have to call one of the three functions described below to restrict the rows
-// returned.
-// 2. The coordiator which has access to all the constraints. They pass an
-// instance of this class and the constraint to filter on to each filter
-// class. Once all the constraints are filtered, the coordinator will extract
-// the underlying row representation from the instance of this class and use
-// that to read rows from the storage.
-class FilteredRowIndex {
- public:
-  FilteredRowIndex(uint32_t start_row, uint32_t end_row);
-
-  // One of the following three functions can be called by the filter classes
-  // to restrict which rows should be returned.
-
-  // Interesects the rows specified by |rows| with the already filtered rows
-  // and updates the index to the intersection.
-  void IntersectRows(std::vector<uint32_t> rows);
-
-  // Calls |fn| on each row index which is currently to be returned and retains
-  // row index if |fn| returns true or discards the row otherwise.
-  template <typename RowPredicate /* (uint32_t) -> bool */>
-  void FilterRows(RowPredicate fn) {
-    PERFETTO_DCHECK(error_.empty());
-
-    switch (mode_) {
-      case Mode::kAllRows:
-        FilterAllRows(fn);
-        break;
-      case Mode::kBitVector:
-        FilterBitVector(fn);
-        break;
-      case Mode::kRowVector:
-        FilterRowVector(fn);
-        break;
-    }
-  }
-
-  // Called when there is some error in the filter operation requested. The
-  // error string is used by the coordinator to report the error to SQLite.
-  void set_error(std::string error) { error_ = std::move(error); }
-
-  // The following functions should only be called by the coordinator classes.
-
-  // Converts this index into a vector of row indicies.
-  // Note: this function leaves the index in a freshly constructed state.
-  std::vector<uint32_t> ToRowVector();
-
-  // Converts this index into a row iterator.
-  // Note: this function leaves the index in a freshly constructed state.
-  std::unique_ptr<RowIterator> ToRowIterator(bool desc);
-
-  // Returns the error from the filter operation invoked. May be empty if no
-  // error occurred.
-  const std::string& error() const { return error_; }
-
- private:
-  enum Mode {
-    kAllRows = 1,
-    kBitVector = 2,
-    kRowVector = 3,
-  };
-
-  template <typename Predicate>
-  void FilterAllRows(Predicate fn) {
-    mode_ = Mode::kBitVector;
-    row_filter_.resize(end_row_ - start_row_, false);
-
-    for (auto i = start_row_; i < end_row_; i++) {
-      if (fn(i))
-        row_filter_[i - start_row_] = true;
-    }
-  }
-
-  template <typename Predicate>
-  void FilterBitVector(Predicate fn) {
-    auto b = row_filter_.begin();
-    auto e = row_filter_.end();
-
-    using std::find;
-    for (auto it = find(b, e, true); it != e; it = find(it + 1, e, true)) {
-      auto filter_idx = static_cast<uint32_t>(std::distance(b, it));
-      auto value_it = start_row_ + filter_idx;
-      *it = fn(value_it);
-    }
-  }
-
-  template <typename Predicate>
-  void FilterRowVector(Predicate fn) {
-    size_t write_idx = 0;
-    for (size_t read_idx = 0; read_idx < rows_.size(); ++read_idx) {
-      uint32_t row = rows_[read_idx];
-      if (fn(row)) {
-        rows_[write_idx++] = row;
-      }
-    }
-    rows_.resize(write_idx);
-  }
-
-  void ConvertBitVectorToRowVector();
-
-  std::vector<uint32_t> TakeRowVector();
-
-  std::vector<bool> TakeBitVector();
-
-  Mode mode_;
-  uint32_t start_row_;
-  uint32_t end_row_;
-
-  // Only non-empty when |mode_| == Mode::kBitVector.
-  std::vector<bool> row_filter_;
-
-  // Only non-empty when |mode_| == Mode::kRowVector.
-  // This vector is sorted.
-  std::vector<uint32_t> rows_;
-
-  std::string error_;
-};
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_FILTERED_ROW_INDEX_H_
diff --git a/src/trace_processor/filtered_row_index_unittest.cc b/src/trace_processor/filtered_row_index_unittest.cc
deleted file mode 100644
index 6810097..0000000
--- a/src/trace_processor/filtered_row_index_unittest.cc
+++ /dev/null
@@ -1,95 +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/filtered_row_index.h"
-
-#include "test/gtest_and_gmock.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace {
-
-using ::testing::ElementsAre;
-
-TEST(FilteredRowIndexUnittest, Noop) {
-  FilteredRowIndex index(1, 4);
-  ASSERT_THAT(index.ToRowVector(), ElementsAre(1, 2, 3));
-}
-
-TEST(FilteredRowIndexUnittest, FilterRows) {
-  FilteredRowIndex index(1, 5);
-  bool in_bound_indices = true;
-  index.FilterRows([&in_bound_indices](uint32_t row) {
-    in_bound_indices = in_bound_indices && row >= 1 && row < 5;
-    return row == 2 || row == 3;
-  });
-  ASSERT_THAT(index.ToRowVector(), ElementsAre(2, 3));
-}
-
-TEST(FilteredRowIndexUnittest, FilterRowsTwice) {
-  FilteredRowIndex index(1, 5);
-  index.FilterRows([](uint32_t row) { return row == 2 || row == 3; });
-  bool in_bound_indices = true;
-  index.FilterRows([&in_bound_indices](uint32_t row) {
-    in_bound_indices = in_bound_indices && (row == 2 || row == 3);
-    return row == 2;
-  });
-  ASSERT_TRUE(in_bound_indices);
-  ASSERT_THAT(index.ToRowVector(), ElementsAre(2));
-}
-
-TEST(FilteredRowIndexUnittest, FilterThenIntersect) {
-  FilteredRowIndex index(1, 5);
-  index.FilterRows([](uint32_t row) { return row == 2 || row == 3; });
-  index.IntersectRows({0, 2, 4, 5, 10});
-  ASSERT_THAT(index.ToRowVector(), ElementsAre(2));
-}
-
-TEST(FilteredRowIndexUnittest, IntersectThenFilter) {
-  FilteredRowIndex index(1, 5);
-  index.IntersectRows({0, 2, 4, 5, 10});
-  index.FilterRows([](uint32_t row) { return row == 2 || row == 3; });
-  ASSERT_THAT(index.ToRowVector(), ElementsAre(2));
-}
-
-TEST(FilteredRowIndexUnittest, Intersect) {
-  FilteredRowIndex index(1, 5);
-  index.IntersectRows({0, 2, 4, 5, 10});
-  ASSERT_THAT(index.ToRowVector(), ElementsAre(2, 4));
-}
-
-TEST(FilteredRowIndexUnittest, IntersectTwice) {
-  FilteredRowIndex index(1, 5);
-  index.IntersectRows({0, 2, 4, 5, 10});
-  index.IntersectRows({4});
-  ASSERT_THAT(index.ToRowVector(), ElementsAre(4));
-}
-
-TEST(FilteredRowIndexUnittest, ToIterator) {
-  FilteredRowIndex index(1, 5);
-  index.IntersectRows({0, 2, 4, 5, 10});
-  auto iterator = index.ToRowIterator(false);
-
-  ASSERT_THAT(iterator->Row(), 2);
-  iterator->NextRow();
-  ASSERT_THAT(iterator->Row(), 4);
-  iterator->NextRow();
-  ASSERT_TRUE(iterator->IsEnd());
-}
-
-}  // namespace
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/forwarding_trace_parser.cc b/src/trace_processor/forwarding_trace_parser.cc
index 89798f2..1d7822e 100644
--- a/src/trace_processor/forwarding_trace_parser.cc
+++ b/src/trace_processor/forwarding_trace_parser.cc
@@ -18,31 +18,18 @@
 
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/string_utils.h"
-#include "src/trace_processor/gzip_trace_parser.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/ninja/ninja_log_parser.h"
 #include "src/trace_processor/importers/proto/proto_trace_parser.h"
 #include "src/trace_processor/importers/proto/proto_trace_tokenizer.h"
 #include "src/trace_processor/trace_sorter.h"
 
-#if PERFETTO_BUILDFLAG(PERFETTO_TP_FUCHSIA)
-#include "src/trace_processor/importers/fuchsia/fuchsia_trace_parser.h"
-#include "src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h"
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_TP_FUCHSIA)
-
-// JSON parsing and exporting is only supported in the standalone and
-// Chromium builds.
-#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON_IMPORT)
-#include "src/trace_processor/importers/json/json_trace_parser.h"
-#include "src/trace_processor/importers/json/json_trace_tokenizer.h"
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_TP_JSON_IMPORT)
-
 namespace perfetto {
 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);
@@ -69,7 +56,7 @@
                                           size_t size) {
   // If this is the first Parse() call, guess the trace type and create the
   // appropriate parser.
-
+  static const int64_t kMaxWindowSize = std::numeric_limits<int64_t>::max();
   if (!reader_) {
     TraceType trace_type;
     {
@@ -80,42 +67,50 @@
     switch (trace_type) {
       case kJsonTraceType: {
         PERFETTO_DLOG("JSON trace detected");
-#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON_IMPORT)
-        reader_.reset(new JsonTraceTokenizer(context_));
-        // JSON traces have no guarantees about the order of events in them.
-        int64_t window_size_ns = std::numeric_limits<int64_t>::max();
-        context_->sorter.reset(new TraceSorter(context_, window_size_ns));
-        context_->parser.reset(new JsonTraceParser(context_));
-#else   // PERFETTO_BUILDFLAG(PERFETTO_TP_JSON_IMPORT)
-        PERFETTO_FATAL("JSON traces not supported.");
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_TP_JSON_IMPORT)
+        if (context_->json_trace_tokenizer && context_->json_trace_parser) {
+          reader_ = std::move(context_->json_trace_tokenizer);
+
+          // JSON traces have no guarantees about the order of events in them.
+          context_->sorter.reset(new TraceSorter(
+              std::move(context_->json_trace_parser), kMaxWindowSize));
+        } else {
+          return util::ErrStatus("JSON support is disabled");
+        }
         break;
       }
       case kProtoTraceType: {
         PERFETTO_DLOG("Proto trace detected");
         // This will be reduced once we read the trace config and we see flush
         // period being set.
-        int64_t window_size_ns = std::numeric_limits<int64_t>::max();
         reader_.reset(new ProtoTraceTokenizer(context_));
-        context_->sorter.reset(new TraceSorter(context_, window_size_ns));
-        context_->parser.reset(new ProtoTraceParser(context_));
+        context_->sorter.reset(new TraceSorter(
+            std::unique_ptr<TraceParser>(new ProtoTraceParser(context_)),
+            kMaxWindowSize));
+        context_->process_tracker->SetPidZeroIgnoredForIdleProcess();
+        break;
+      }
+      case kNinjaLogTraceType: {
+        PERFETTO_DLOG("Ninja log detected");
+        reader_.reset(new NinjaLogParser(context_));
         break;
       }
       case kFuchsiaTraceType: {
-#if PERFETTO_BUILDFLAG(PERFETTO_TP_FUCHSIA)
         PERFETTO_DLOG("Fuchsia trace detected");
-        // Fuschia traces can have massively out of order events.
-        int64_t window_size_ns = std::numeric_limits<int64_t>::max();
-        reader_.reset(new FuchsiaTraceTokenizer(context_));
-        context_->sorter.reset(new TraceSorter(context_, window_size_ns));
-        context_->parser.reset(new FuchsiaTraceParser(context_));
-#else   // PERFETTO_BUILDFLAG(PERFETTO_TP_FUCHSIA)
-        PERFETTO_FATAL("Fuchsia traces not supported.");
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_TP_FUCHSIA)
+        if (context_->fuchsia_trace_parser &&
+            context_->fuchsia_trace_tokenizer) {
+          reader_ = std::move(context_->fuchsia_trace_tokenizer);
+
+          // Fuschia traces can have massively out of order events.
+          context_->sorter.reset(new TraceSorter(
+              std::move(context_->fuchsia_trace_parser), kMaxWindowSize));
+        } else {
+          return util::ErrStatus("Fuchsia support is disabled");
+        }
         break;
       }
       case kSystraceTraceType:
         PERFETTO_DLOG("Systrace trace detected");
+        context_->process_tracker->SetPidZeroIgnoredForIdleProcess();
         if (context_->systrace_trace_parser) {
           reader_ = std::move(context_->systrace_trace_parser);
           break;
@@ -123,21 +118,18 @@
           return util::ErrStatus("Systrace support is disabled");
         }
       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
+        if (trace_type == kGzipTraceType) {
+          PERFETTO_DLOG("gzip trace detected");
+        } else {
+          PERFETTO_DLOG("ctrace trace detected");
+        }
+        if (context_->gzip_trace_parser) {
+          reader_ = std::move(context_->gzip_trace_parser);
+          break;
+        } else {
+          return util::ErrStatus(kNoZlibErr);
+        }
       case kUnknownTraceType:
         return util::ErrStatus("Unknown trace type provided");
     }
@@ -146,21 +138,25 @@
   return reader_->Parse(std::move(data), size);
 }
 
+void ForwardingTraceParser::NotifyEndOfFile() {
+  reader_->NotifyEndOfFile();
+}
+
 TraceType GuessTraceType(const uint8_t* data, size_t size) {
   if (size == 0)
     return kUnknownTraceType;
   std::string start(reinterpret_cast<const char*>(data),
                     std::min<size_t>(size, 20));
-  std::string start_minus_white_space = RemoveWhitespace(start);
-  if (base::StartsWith(start_minus_white_space, "{\"traceEvents\":["))
-    return kJsonTraceType;
-  if (base::StartsWith(start_minus_white_space, "[{"))
-    return kJsonTraceType;
   if (size >= 8) {
     uint64_t first_word = *reinterpret_cast<const uint64_t*>(data);
     if (first_word == kFuchsiaMagicNumber)
       return kFuchsiaTraceType;
   }
+  std::string start_minus_white_space = RemoveWhitespace(start);
+  if (base::StartsWith(start_minus_white_space, "{"))
+    return kJsonTraceType;
+  if (base::StartsWith(start_minus_white_space, "[{"))
+    return kJsonTraceType;
 
   // Systrace with header but no leading HTML.
   if (base::StartsWith(start, "# tracer"))
@@ -175,6 +171,10 @@
   if (start.find("TRACE:") != std::string::npos)
     return kCtraceTraceType;
 
+  // Ninja's buils log (.ninja_log).
+  if (base::StartsWith(start, "# ninja log"))
+    return kNinjaLogTraceType;
+
   // Systrace with no header or leading HTML.
   if (base::StartsWith(start, " "))
     return kSystraceTraceType;
diff --git a/src/trace_processor/forwarding_trace_parser.h b/src/trace_processor/forwarding_trace_parser.h
index f7216b3..8b8a8bb 100644
--- a/src/trace_processor/forwarding_trace_parser.h
+++ b/src/trace_processor/forwarding_trace_parser.h
@@ -19,7 +19,7 @@
 
 #include "src/trace_processor/chunked_trace_reader.h"
 
-#include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -32,6 +32,7 @@
   kSystraceTraceType,
   kGzipTraceType,
   kCtraceTraceType,
+  kNinjaLogTraceType,
 };
 
 TraceType GuessTraceType(const uint8_t* data, size_t size);
@@ -43,6 +44,7 @@
 
   // ChunkedTraceReader implementation
   util::Status Parse(std::unique_ptr<uint8_t[]>, size_t) override;
+  void NotifyEndOfFile() override;
 
  private:
   TraceProcessorContext* const context_;
diff --git a/src/trace_processor/forwarding_trace_parser_unittest.cc b/src/trace_processor/forwarding_trace_parser_unittest.cc
index 0d6877c..78eb404 100644
--- a/src/trace_processor/forwarding_trace_parser_unittest.cc
+++ b/src/trace_processor/forwarding_trace_parser_unittest.cc
@@ -32,11 +32,17 @@
   EXPECT_EQ(kJsonTraceType, GuessTraceType(prefix, sizeof(prefix)));
 }
 
+TEST(TraceProcessorImplTest, GuessTraceType_Ninja) {
+  const uint8_t prefix[] = "# ninja log v5\n";
+  EXPECT_EQ(kNinjaLogTraceType, GuessTraceType(prefix, sizeof(prefix)));
+}
+
 TEST(TraceProcessorImplTest, GuessTraceType_JsonWithSpaces) {
   const uint8_t prefix[] = "\n{ \"traceEvents\": [";
   EXPECT_EQ(kJsonTraceType, GuessTraceType(prefix, sizeof(prefix)));
 }
 
+// Some Android build traces do not contain the wrapper. See b/118826940
 TEST(TraceProcessorImplTest, GuessTraceType_JsonMissingTraceEvents) {
   const uint8_t prefix[] = "[{";
   EXPECT_EQ(kJsonTraceType, GuessTraceType(prefix, sizeof(prefix)));
diff --git a/src/trace_processor/importers/BUILD.gn b/src/trace_processor/importers/BUILD.gn
new file mode 100644
index 0000000..d3d8482
--- /dev/null
+++ b/src/trace_processor/importers/BUILD.gn
@@ -0,0 +1,61 @@
+# 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")
+
+source_set("common") {
+  sources = [
+    "common/args_tracker.cc",
+    "common/args_tracker.h",
+    "common/clock_tracker.cc",
+    "common/clock_tracker.h",
+    "common/event_tracker.cc",
+    "common/event_tracker.h",
+    "common/global_args_tracker.cc",
+    "common/global_args_tracker.h",
+    "common/process_tracker.cc",
+    "common/process_tracker.h",
+    "common/slice_tracker.cc",
+    "common/slice_tracker.h",
+    "common/track_tracker.cc",
+    "common/track_tracker.h",
+  ]
+  deps = [
+    "../../../gn:default_deps",
+    "../../../protos/perfetto/trace:zero",
+    "../../../protos/perfetto/trace/profiling:zero",
+    "../../base",
+    "../storage",
+    "../types",
+  ]
+}
+
+source_set("unittests") {
+  sources = [
+    "common/clock_tracker_unittest.cc",
+    "common/event_tracker_unittest.cc",
+    "common/process_tracker_unittest.cc",
+    "common/slice_tracker_unittest.cc",
+  ]
+  testonly = true
+  deps = [
+    ":common",
+    "../../../gn:default_deps",
+    "../../../gn:gtest_and_gmock",
+    "../../../protos/perfetto/trace:zero",
+    "../../base",
+    "../storage",
+    "../types",
+  ]
+}
diff --git a/src/trace_processor/register_additional_modules.cc b/src/trace_processor/importers/additional_modules.cc
similarity index 88%
rename from src/trace_processor/register_additional_modules.cc
rename to src/trace_processor/importers/additional_modules.cc
index ad14f74..1627893 100644
--- a/src/trace_processor/register_additional_modules.cc
+++ b/src/trace_processor/importers/additional_modules.cc
@@ -14,13 +14,12 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/register_additional_modules.h"
+#include "src/trace_processor/importers/additional_modules.h"
 #include "src/trace_processor/importers/ftrace/ftrace_module_impl.h"
 #include "src/trace_processor/importers/proto/android_probes_module.h"
 #include "src/trace_processor/importers/proto/graphics_event_module.h"
 #include "src/trace_processor/importers/proto/heap_graph_module.h"
 #include "src/trace_processor/importers/proto/system_probes_module.h"
-#include "src/trace_processor/importers/systrace/systrace_trace_parser.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -35,8 +34,6 @@
   // ftrace packets. So we need to store a pointer to it separately.
   context->ftrace_module =
       static_cast<FtraceModule*>(context->modules.back().get());
-
-  context->systrace_trace_parser.reset(new SystraceTraceParser(context));
 }
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/register_additional_modules.h b/src/trace_processor/importers/additional_modules.h
similarity index 75%
rename from src/trace_processor/register_additional_modules.h
rename to src/trace_processor/importers/additional_modules.h
index 3b888d5..8040b64 100644
--- a/src/trace_processor/register_additional_modules.h
+++ b/src/trace_processor/importers/additional_modules.h
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_REGISTER_ADDITIONAL_MODULES_H_
-#define SRC_TRACE_PROCESSOR_REGISTER_ADDITIONAL_MODULES_H_
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_ADDITIONAL_MODULES_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_ADDITIONAL_MODULES_H_
 
-#include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -27,4 +27,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_REGISTER_ADDITIONAL_MODULES_H_
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_ADDITIONAL_MODULES_H_
diff --git a/src/trace_processor/args_tracker.cc b/src/trace_processor/importers/common/args_tracker.cc
similarity index 75%
rename from src/trace_processor/args_tracker.cc
rename to src/trace_processor/importers/common/args_tracker.cc
index a0114ce..c7a5b97 100644
--- a/src/trace_processor/args_tracker.cc
+++ b/src/trace_processor/importers/common/args_tracker.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/args_tracker.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
 
 #include <algorithm>
 
@@ -31,7 +31,8 @@
                          uint32_t row,
                          StringId flat_key,
                          StringId key,
-                         Variadic value) {
+                         Variadic value,
+                         UpdatePolicy update_policy) {
   args_.emplace_back();
 
   auto* rid_arg = &args_.back();
@@ -40,6 +41,7 @@
   rid_arg->flat_key = flat_key;
   rid_arg->key = key;
   rid_arg->value = value;
+  rid_arg->update_policy = update_policy;
 }
 
 void ArgsTracker::Flush() {
@@ -52,10 +54,15 @@
   // rowids.
   auto comparator = [](const Arg& f, const Arg& s) {
     // 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;
+    // block and that args within one arg set are sorted by key, 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, and then by key.
+    if (f.column == s.column && f.row == s.row)
+      return f.key < s.key;
+    if (f.column == s.column)
+      return f.row < s.row;
+    return f.column < s.column;
   };
   std::stable_sort(args_.begin(), args_.end(), comparator);
 
@@ -89,5 +96,9 @@
 
 ArgsTracker::BoundInserter::~BoundInserter() {}
 
+ArgsTracker::BoundInserter::BoundInserter(BoundInserter&&) = default;
+ArgsTracker::BoundInserter& ArgsTracker::BoundInserter::operator=(
+    BoundInserter&&) = default;
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/args_tracker.h b/src/trace_processor/importers/common/args_tracker.h
similarity index 63%
rename from src/trace_processor/args_tracker.h
rename to src/trace_processor/importers/common/args_tracker.h
index 9acccfb..ebcc8f9 100644
--- a/src/trace_processor/args_tracker.h
+++ b/src/trace_processor/importers/common/args_tracker.h
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_ARGS_TRACKER_H_
-#define SRC_TRACE_PROCESSOR_ARGS_TRACKER_H_
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_ARGS_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_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/importers/common/global_args_tracker.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 #include "src/trace_processor/types/variadic.h"
 
 namespace perfetto {
@@ -29,6 +29,8 @@
 // allows args to pushed as a group into storage.
 class ArgsTracker {
  public:
+  using UpdatePolicy = GlobalArgsTracker::UpdatePolicy;
+
   // Stores the table and row at creation time which args are associated with.
   // This allows callers to directly add args without repeating the row the
   // args should be associated with.
@@ -36,17 +38,44 @@
    public:
     virtual ~BoundInserter();
 
+    BoundInserter(BoundInserter&&);
+    BoundInserter& operator=(BoundInserter&&);
+
+    BoundInserter(const BoundInserter&) = delete;
+    BoundInserter& operator=(const BoundInserter&) = delete;
+
     // Adds an arg with the same key and flat_key.
-    BoundInserter& AddArg(StringId key, Variadic v) {
-      return AddArg(key, key, v);
+    BoundInserter& AddArg(
+        StringId key,
+        Variadic v,
+        UpdatePolicy update_policy = UpdatePolicy::kAddOrUpdate) {
+      return AddArg(key, key, v, update_policy);
     }
 
-    // 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);
+    virtual BoundInserter& AddArg(
+        StringId flat_key,
+        StringId key,
+        Variadic v,
+        UpdatePolicy update_policy = UpdatePolicy::kAddOrUpdate) {
+      args_tracker_->AddArg(arg_set_id_column_, row_, flat_key, key, v,
+                            update_policy);
       return *this;
     }
 
+    // IncrementArrayEntryIndex() and GetNextArrayEntryIndex() provide a way to
+    // track the next array index for an array under a specific key.
+    void IncrementArrayEntryIndex(StringId key) {
+      // Zero-initializes |key| in the map if it doesn't exist yet.
+      args_tracker_
+          ->array_indexes_[std::make_tuple(arg_set_id_column_, row_, key)]++;
+    }
+
+    size_t GetNextArrayEntryIndex(StringId key) {
+      // Zero-initializes |key| in the map if it doesn't exist yet.
+      return args_tracker_
+          ->array_indexes_[std::make_tuple(arg_set_id_column_, row_, key)];
+    }
+
    protected:
     BoundInserter(ArgsTracker* args_tracker,
                   Column* arg_set_id_column,
@@ -111,13 +140,18 @@
               uint32_t row,
               StringId flat_key,
               StringId key,
-              Variadic);
+              Variadic,
+              UpdatePolicy);
 
   std::vector<GlobalArgsTracker::Arg> args_;
   TraceProcessorContext* const context_;
+
+  using ArrayKeyTuple =
+      std::tuple<Column* /*arg_set_id*/, uint32_t /*row*/, StringId /*key*/>;
+  std::map<ArrayKeyTuple, size_t /*next_index*/> array_indexes_;
 };
 
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_ARGS_TRACKER_H_
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_ARGS_TRACKER_H_
diff --git a/src/trace_processor/clock_tracker.cc b/src/trace_processor/importers/common/clock_tracker.cc
similarity index 82%
rename from src/trace_processor/clock_tracker.cc
rename to src/trace_processor/importers/common/clock_tracker.cc
index 696a110..1ba5c5e 100644
--- a/src/trace_processor/clock_tracker.cc
+++ b/src/trace_processor/importers/common/clock_tracker.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/clock_tracker.h"
+#include "src/trace_processor/importers/common/clock_tracker.h"
 
 #include <inttypes.h>
 
@@ -23,8 +23,8 @@
 
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/hash.h"
-#include "src/trace_processor/trace_processor_context.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 #include "protos/perfetto/trace/clock_snapshot.pbzero.h"
 
@@ -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/importers/common/clock_tracker.h
similarity index 85%
rename from src/trace_processor/clock_tracker.h
rename to src/trace_processor/importers/common/clock_tracker.h
index c261249..604d461 100644
--- a/src/trace_processor/clock_tracker.h
+++ b/src/trace_processor/importers/common/clock_tracker.h
@@ -14,13 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_CLOCK_TRACKER_H_
-#define SRC_TRACE_PROCESSOR_CLOCK_TRACKER_H_
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_CLOCK_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_CLOCK_TRACKER_H_
 
 #include <stdint.h>
 
 #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,10 +295,13 @@
   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;
 };
 
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_CLOCK_TRACKER_H_
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_CLOCK_TRACKER_H_
diff --git a/src/trace_processor/clock_tracker_unittest.cc b/src/trace_processor/importers/common/clock_tracker_unittest.cc
similarity index 81%
rename from src/trace_processor/clock_tracker_unittest.cc
rename to src/trace_processor/importers/common/clock_tracker_unittest.cc
index e1628af..d259bac 100644
--- a/src/trace_processor/clock_tracker_unittest.cc
+++ b/src/trace_processor/importers/common/clock_tracker_unittest.cc
@@ -14,11 +14,13 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/clock_tracker.h"
+#include "src/trace_processor/importers/common/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"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 #include "test/gtest_and_gmock.h"
 
 #include "protos/perfetto/trace/clock_snapshot.pbzero.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/event_tracker.cc b/src/trace_processor/importers/common/event_tracker.cc
similarity index 86%
rename from src/trace_processor/event_tracker.cc
rename to src/trace_processor/importers/common/event_tracker.cc
index cd7c9e4..3130566 100644
--- a/src/trace_processor/event_tracker.cc
+++ b/src/trace_processor/importers/common/event_tracker.cc
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/event_tracker.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
 
 #include <math.h>
 
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/utils.h"
-#include "src/trace_processor/args_tracker.h"
-#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/track_tracker.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/storage/stats.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 #include "src/trace_processor/types/variadic.h"
 
 namespace perfetto {
@@ -64,7 +64,7 @@
   max_timestamp_ = timestamp;
 
   auto* counter_values = context_->storage->mutable_counter_table();
-  return counter_values->Insert({timestamp, track_id.value, value});
+  return counter_values->Insert({timestamp, track_id, value}).id;
 }
 
 InstantId EventTracker::PushInstant(int64_t timestamp,
@@ -77,15 +77,16 @@
   if (resolve_utid_to_upid) {
     auto ref_type_id = context_->storage->InternString(
         GetRefTypeStringMap()[static_cast<size_t>(RefType::kRefUpid)]);
-    id = instants->Insert({timestamp, name_id, 0, ref_type_id});
+    auto id_and_row = instants->Insert({timestamp, name_id, 0, ref_type_id});
+    id = id_and_row.id;
     PendingUpidResolutionInstant pending;
-    pending.row = *instants->id().IndexOf(id);
+    pending.row = id_and_row.row;
     pending.utid = static_cast<UniqueTid>(ref);
     pending_upid_resolution_instant_.emplace_back(pending);
   } else {
     auto ref_type_id = context_->storage->InternString(
         GetRefTypeStringMap()[static_cast<size_t>(ref_type)]);
-    id = instants->Insert({timestamp, name_id, ref, ref_type_id});
+    id = instants->Insert({timestamp, name_id, ref, ref_type_id}).id;
   }
   return id;
 }
@@ -100,7 +101,7 @@
     TrackId id = context_->track_tracker->InternProcessCounterTrack(
         pending_counter.name_id, upid);
     context_->storage->mutable_counter_table()->mutable_track_id()->Set(
-        pending_counter.row, id.value);
+        pending_counter.row, id);
   }
 
   for (const auto& pending_instant : pending_upid_resolution_instant_) {
diff --git a/src/trace_processor/event_tracker.h b/src/trace_processor/importers/common/event_tracker.h
similarity index 92%
rename from src/trace_processor/event_tracker.h
rename to src/trace_processor/importers/common/event_tracker.h
index bde0588..26c3785 100644
--- a/src/trace_processor/event_tracker.h
+++ b/src/trace_processor/importers/common/event_tracker.h
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_EVENT_TRACKER_H_
-#define SRC_TRACE_PROCESSOR_EVENT_TRACKER_H_
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_EVENT_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_EVENT_TRACKER_H_
 
 #include <array>
 #include <limits>
 
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/ext/base/utils.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/trace_storage.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -101,4 +101,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_EVENT_TRACKER_H_
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_EVENT_TRACKER_H_
diff --git a/src/trace_processor/importers/common/event_tracker_unittest.cc b/src/trace_processor/importers/common/event_tracker_unittest.cc
new file mode 100644
index 0000000..66fc899
--- /dev/null
+++ b/src/trace_processor/importers/common/event_tracker_unittest.cc
@@ -0,0 +1,74 @@
+/*
+ * 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/importers/common/event_tracker.h"
+
+#include "perfetto/base/logging.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Invoke;
+
+class EventTrackerTest : public ::testing::Test {
+ 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));
+    context.track_tracker.reset(new TrackTracker(&context));
+  }
+
+ protected:
+  TraceProcessorContext context;
+};
+
+TEST_F(EventTrackerTest, CounterDuration) {
+  uint32_t cpu = 3;
+  int64_t timestamp = 100;
+  StringId name_id = kNullStringId;
+
+  TrackId track = context.track_tracker->InternCpuCounterTrack(name_id, cpu);
+  context.event_tracker->PushCounter(timestamp, 1000, track);
+  context.event_tracker->PushCounter(timestamp + 1, 4000, track);
+  context.event_tracker->PushCounter(timestamp + 3, 5000, track);
+  context.event_tracker->PushCounter(timestamp + 9, 1000, track);
+
+  ASSERT_EQ(context.storage->counter_track_table().row_count(), 1ul);
+
+  ASSERT_EQ(context.storage->counter_table().row_count(), 4ul);
+  ASSERT_EQ(context.storage->counter_table().ts()[0], timestamp);
+  ASSERT_DOUBLE_EQ(context.storage->counter_table().value()[0], 1000);
+
+  ASSERT_EQ(context.storage->counter_table().ts()[1], timestamp + 1);
+  ASSERT_DOUBLE_EQ(context.storage->counter_table().value()[1], 4000);
+
+  ASSERT_EQ(context.storage->counter_table().ts()[2], timestamp + 3);
+  ASSERT_DOUBLE_EQ(context.storage->counter_table().value()[2], 5000);
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/global_args_tracker.cc b/src/trace_processor/importers/common/global_args_tracker.cc
similarity index 91%
rename from src/trace_processor/global_args_tracker.cc
rename to src/trace_processor/importers/common/global_args_tracker.cc
index 9966108..dc1343f 100644
--- a/src/trace_processor/global_args_tracker.cc
+++ b/src/trace_processor/importers/common/global_args_tracker.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/global_args_tracker.h"
+#include "src/trace_processor/importers/common/global_args_tracker.h"
 
 namespace perfetto {
 namespace trace_processor {
diff --git a/src/trace_processor/global_args_tracker.h b/src/trace_processor/importers/common/global_args_tracker.h
similarity index 71%
rename from src/trace_processor/global_args_tracker.h
rename to src/trace_processor/importers/common/global_args_tracker.h
index 74c5152..4a0417d 100644
--- a/src/trace_processor/global_args_tracker.h
+++ b/src/trace_processor/importers/common/global_args_tracker.h
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_GLOBAL_ARGS_TRACKER_H_
-#define SRC_TRACE_PROCESSOR_GLOBAL_ARGS_TRACKER_H_
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_GLOBAL_ARGS_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_GLOBAL_ARGS_TRACKER_H_
 
-#include "src/trace_processor/trace_processor_context.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 #include "src/trace_processor/types/variadic.h"
 
 namespace perfetto {
@@ -30,6 +30,12 @@
 // that purpose.
 class GlobalArgsTracker {
  public:
+  // How to behave if two or more args with the same key were added into the
+  // same ArgSet. If |kSkipIfExists|, the arg will be ignored if another arg
+  // with the same key already exists. If |kAddOrUpdate|, any existing arg with
+  // the same key will be overridden.
+  enum class UpdatePolicy { kSkipIfExists, kAddOrUpdate };
+
   struct Arg {
     StringId flat_key = kNullStringId;
     StringId key = kNullStringId;
@@ -37,6 +43,7 @@
 
     Column* column;
     uint32_t row;
+    UpdatePolicy update_policy = UpdatePolicy::kAddOrUpdate;
   };
 
   struct ArgHasher {
@@ -73,11 +80,34 @@
 
   GlobalArgsTracker(TraceProcessorContext* context);
 
+  // Assumes that the interval [begin, end) of |args| is sorted by keys.
   ArgSetId AddArgSet(const std::vector<Arg>& args,
                      uint32_t begin,
                      uint32_t end) {
-    base::Hash hash;
+    std::vector<uint32_t> valid_indexes;
+    valid_indexes.reserve(end - begin);
+
+    // TODO(eseckler): Also detect "invalid" key combinations in args sets (e.g.
+    // "foo" and "foo.bar" in the same arg set)?
     for (uint32_t i = begin; i < end; i++) {
+      if (!valid_indexes.empty() &&
+          args[valid_indexes.back()].key == args[i].key) {
+        // Last arg had the same key as this one. In case of kSkipIfExists, skip
+        // this arg. In case of kAddOrUpdate, remove the last arg and add this
+        // arg instead.
+        if (args[i].update_policy == UpdatePolicy::kSkipIfExists) {
+          continue;
+        } else {
+          PERFETTO_DCHECK(args[i].update_policy == UpdatePolicy::kAddOrUpdate);
+          valid_indexes.pop_back();
+        }
+      }
+
+      valid_indexes.push_back(i);
+    }
+
+    base::Hash hash;
+    for (uint32_t i : valid_indexes) {
       hash.Update(ArgHasher()(args[i]));
     }
 
@@ -91,7 +121,7 @@
     // 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++) {
+    for (uint32_t i : valid_indexes) {
       const auto& arg = args[i];
 
       tables::ArgTable::Row row;
@@ -138,4 +168,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_GLOBAL_ARGS_TRACKER_H_
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_GLOBAL_ARGS_TRACKER_H_
diff --git a/src/trace_processor/process_tracker.cc b/src/trace_processor/importers/common/process_tracker.cc
similarity index 96%
rename from src/trace_processor/process_tracker.cc
rename to src/trace_processor/importers/common/process_tracker.cc
index 27668ef..801b532 100644
--- a/src/trace_processor/process_tracker.cc
+++ b/src/trace_processor/importers/common/process_tracker.cc
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/process_tracker.h"
-#include "src/trace_processor/stats.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/storage/stats.h"
 
 #include <utility>
 
@@ -25,11 +25,7 @@
 namespace trace_processor {
 
 ProcessTracker::ProcessTracker(TraceProcessorContext* context)
-    : context_(context) {
-  // Create a mapping from (t|p)id 0 -> u(t|p)id 0 for the idle process.
-  tids_.emplace(0, std::vector<UniqueTid>{0});
-  pids_.emplace(0, 0);
-}
+    : context_(context) {}
 
 ProcessTracker::~ProcessTracker() = default;
 
@@ -42,7 +38,7 @@
   row.start_ts = timestamp;
 
   auto* thread_table = context_->storage->mutable_thread_table();
-  UniqueTid new_utid = thread_table->Insert(row).value;
+  UniqueTid new_utid = thread_table->Insert(row).row;
   tids_[tid].emplace_back(new_utid);
   return new_utid;
 }
@@ -258,6 +254,10 @@
 void ProcessTracker::SetProcessUid(UniquePid upid, uint32_t uid) {
   auto* process_table = context_->storage->mutable_process_table();
   process_table->mutable_uid()->Set(upid, uid);
+
+  // The notion of the app ID (as derived from the uid) is defined in
+  // frameworks/base/core/java/android/os/UserHandle.java
+  process_table->mutable_android_appid()->Set(upid, uid % 100000);
 }
 
 void ProcessTracker::SetProcessNameIfUnset(UniquePid upid,
@@ -289,7 +289,7 @@
   } else {
     tables::ProcessTable::Row row;
     row.pid = pid;
-    upid = context_->storage->mutable_process_table()->Insert(row).value;
+    upid = context_->storage->mutable_process_table()->Insert(row).row;
 
     pids_.emplace(pid, upid);
 
@@ -399,5 +399,11 @@
   }  // while (!resolved_utids.empty())
 }
 
+void ProcessTracker::SetPidZeroIgnoredForIdleProcess() {
+  // Create a mapping from (t|p)id 0 -> u(t|p)id 0 for the idle process.
+  tids_.emplace(0, std::vector<UniqueTid>{0});
+  pids_.emplace(0, 0);
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/process_tracker.h b/src/trace_processor/importers/common/process_tracker.h
similarity index 91%
rename from src/trace_processor/process_tracker.h
rename to src/trace_processor/importers/common/process_tracker.h
index d9141c4..d648e92 100644
--- a/src/trace_processor/process_tracker.h
+++ b/src/trace_processor/importers/common/process_tracker.h
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_PROCESS_TRACKER_H_
-#define SRC_TRACE_PROCESSOR_PROCESS_TRACKER_H_
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_PROCESS_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_PROCESS_TRACKER_H_
 
 #include <tuple>
 
 #include "perfetto/ext/base/string_view.h"
-#include "src/trace_processor/trace_processor_context.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -52,6 +52,9 @@
                            uint32_t tid,
                            StringId thread_name_id);
 
+  // Returns whether a thread is considered alive by the process tracker.
+  bool IsThreadAlive(UniqueTid utid);
+
   // Called when sched_process_exit is observed. This forces the tracker to
   // end the thread lifetime for the utid associated with the given tid.
   void EndThread(int64_t timestamp, uint32_t tid);
@@ -125,6 +128,11 @@
   // is irrelevant, Associate(A, B) has the same effect of Associate(B, A).
   void AssociateThreads(UniqueTid, UniqueTid);
 
+  // Creates the mapping from tid 0 <-> utid 0 and pid 0 <-> upid 0. This is
+  // done for Linux-based system traces (proto or ftrace format) as for these
+  // traces, we always have the "swapper" (idle) process having tid/pid 0.
+  void SetPidZeroIgnoredForIdleProcess();
+
  private:
   // Returns the utid of a thread having |tid| and |pid| as the parent process.
   // pid == base::nullopt matches all processes.
@@ -132,9 +140,6 @@
   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.
@@ -165,4 +170,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_PROCESS_TRACKER_H_
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_PROCESS_TRACKER_H_
diff --git a/src/trace_processor/process_tracker_unittest.cc b/src/trace_processor/importers/common/process_tracker_unittest.cc
similarity index 77%
rename from src/trace_processor/process_tracker_unittest.cc
rename to src/trace_processor/importers/common/process_tracker_unittest.cc
index e626681..f9bad28 100644
--- a/src/trace_processor/process_tracker_unittest.cc
+++ b/src/trace_processor/importers/common/process_tracker_unittest.cc
@@ -14,12 +14,11 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/process_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
 
 #include "perfetto/base/logging.h"
-#include "src/trace_processor/args_tracker.h"
-#include "src/trace_processor/event_tracker.h"
-#include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
 #include "test/gtest_and_gmock.h"
 
 namespace perfetto {
@@ -89,31 +88,6 @@
   ASSERT_EQ(name, "test");
 }
 
-TEST_F(ProcessTrackerTest, UpdateThreadMatch) {
-  uint32_t cpu = 3;
-  int64_t timestamp = 100;
-  int64_t prev_state = 32;
-  static const char kCommProc1[] = "process1";
-  static const char kCommProc2[] = "process2";
-  int32_t prio = 1024;
-  SchedEventTracker* sched_tracker = SchedEventTracker::GetOrCreate(&context);
-
-  sched_tracker->PushSchedSwitch(cpu, timestamp, /*tid=*/1, kCommProc2, prio,
-                                 prev_state,
-                                 /*tid=*/4, kCommProc1, prio);
-  sched_tracker->PushSchedSwitch(cpu, timestamp + 1, /*tid=*/4, kCommProc1,
-                                 prio, prev_state,
-                                 /*tid=*/1, kCommProc2, prio);
-
-  context.process_tracker->SetProcessMetadata(2, base::nullopt, "test");
-  context.process_tracker->UpdateThread(4, 2);
-
-  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);
 
diff --git a/src/trace_processor/slice_tracker.cc b/src/trace_processor/importers/common/slice_tracker.cc
similarity index 84%
rename from src/trace_processor/slice_tracker.cc
rename to src/trace_processor/importers/common/slice_tracker.cc
index 39f7d96..2cee75f 100644
--- a/src/trace_processor/slice_tracker.cc
+++ b/src/trace_processor/importers/common/slice_tracker.cc
@@ -18,11 +18,11 @@
 
 #include <stdint.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 "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -42,10 +42,10 @@
                                              StringId category,
                                              StringId name,
                                              SetArgsCallback args_callback) {
-  tables::SliceTable::Row row(timestamp, kPendingDuration, track_id.value,
-                              category, name);
+  tables::SliceTable::Row row(timestamp, kPendingDuration, track_id, category,
+                              name);
   return StartSlice(timestamp, track_id, args_callback, [this, &row]() {
-    return context_->storage->mutable_slice_table()->Insert(row);
+    return context_->storage->mutable_slice_table()->Insert(row).id;
   });
 }
 
@@ -55,8 +55,21 @@
   // 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);
+  StartSlice(row.ts, row.track_id, args_callback, [this, &row]() {
+    return context_->storage->mutable_gpu_slice_table()->Insert(row).id;
+  });
+}
+
+void SliceTracker::BeginFrameEvent(tables::GraphicsFrameSliceTable::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, row.track_id, args_callback, [this, &row]() {
+    return context_->storage->mutable_graphics_frame_slice_table()
+        ->Insert(row)
+        .id;
   });
 }
 
@@ -68,10 +81,9 @@
                                               SetArgsCallback args_callback) {
   PERFETTO_DCHECK(duration >= 0);
 
-  tables::SliceTable::Row row(timestamp, duration, track_id.value, category,
-                              name);
+  tables::SliceTable::Row row(timestamp, duration, track_id, category, name);
   return StartSlice(timestamp, track_id, args_callback, [this, &row]() {
-    return context_->storage->mutable_slice_table()->Insert(row);
+    return context_->storage->mutable_slice_table()->Insert(row).id;
   });
 }
 
@@ -80,7 +92,19 @@
   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);
+    return context_->storage->mutable_gpu_slice_table()->Insert(row).id;
+  });
+}
+
+void SliceTracker::ScopedFrameEvent(
+    const tables::GraphicsFrameSliceTable::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_graphics_frame_slice_table()
+        ->Insert(row)
+        .id;
   });
 }
 
@@ -106,6 +130,15 @@
   });
 }
 
+base::Optional<SliceId> SliceTracker::EndFrameEvent(
+    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,
@@ -129,6 +162,9 @@
   }
   int64_t parent_stack_id =
       depth == 0 ? 0 : slices->stack_id()[stack->back().first];
+  base::Optional<tables::SliceTable::Id> parent_id =
+      depth == 0 ? base::nullopt
+                 : base::make_optional(slices->id()[stack->back().first]);
 
   SliceId id = inserter();
   uint32_t slice_idx = *slices->id().IndexOf(id);
@@ -139,6 +175,8 @@
   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 (parent_id)
+    slices->mutable_parent_id()->Set(slice_idx, *parent_id);
 
   if (args_callback) {
     ArgsTracker* tracker = &stack->back().second;
@@ -178,7 +216,7 @@
   slices->mutable_dur()->Set(slice_idx, timestamp - slices->ts()[slice_idx]);
 
   if (args_callback) {
-    ArgsTracker* tracker = &stack.back().second;
+    ArgsTracker* tracker = &stack[stack_idx.value()].second;
     auto bound_inserter = tracker->AddArgsTo(slices->id()[slice_idx]);
     args_callback(&bound_inserter);
   }
@@ -202,8 +240,8 @@
     if (slices->dur()[slice_idx] != kPendingDuration)
       continue;
     const StringId& other_category = slices->category()[slice_idx];
-    if (!category.is_null() && !other_category.is_null() &&
-        category != other_category)
+    if (!category.is_null() &&
+        (other_category.is_null() || category != other_category))
       continue;
     const StringId& other_name = slices->name()[slice_idx];
     if (!name.is_null() && !other_name.is_null() && name != other_name)
diff --git a/src/trace_processor/slice_tracker.h b/src/trace_processor/importers/common/slice_tracker.h
similarity index 81%
rename from src/trace_processor/slice_tracker.h
rename to src/trace_processor/importers/common/slice_tracker.h
index 36fe5ef..16bf9ed 100644
--- a/src/trace_processor/slice_tracker.h
+++ b/src/trace_processor/importers/common/slice_tracker.h
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_SLICE_TRACKER_H_
-#define SRC_TRACE_PROCESSOR_SLICE_TRACKER_H_
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_SLICE_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_SLICE_TRACKER_H_
 
 #include <stdint.h>
 
-#include "src/trace_processor/args_tracker.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/storage/trace_storage.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -46,6 +46,9 @@
   void BeginGpu(tables::GpuSliceTable::Row row,
                 SetArgsCallback args_callback = SetArgsCallback());
 
+  void BeginFrameEvent(tables::GraphicsFrameSliceTable::Row row,
+                       SetArgsCallback args_callback = SetArgsCallback());
+
   // virtual for testing
   virtual base::Optional<uint32_t> Scoped(
       int64_t timestamp,
@@ -58,6 +61,9 @@
   void ScopedGpu(const tables::GpuSliceTable::Row& row,
                  SetArgsCallback args_callback = SetArgsCallback());
 
+  void ScopedFrameEvent(const tables::GraphicsFrameSliceTable::Row& row,
+                        SetArgsCallback args_callback = SetArgsCallback());
+
   // virtual for testing
   virtual base::Optional<uint32_t> End(
       int64_t timestamp,
@@ -73,6 +79,11 @@
       TrackId track_id,
       SetArgsCallback args_callback = SetArgsCallback());
 
+  base::Optional<SliceId> EndFrameEvent(
+      int64_t ts,
+      TrackId track_id,
+      SetArgsCallback args_callback = SetArgsCallback());
+
   void FlushPendingSlices();
 
  private:
@@ -109,4 +120,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_SLICE_TRACKER_H_
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_SLICE_TRACKER_H_
diff --git a/src/trace_processor/slice_tracker_unittest.cc b/src/trace_processor/importers/common/slice_tracker_unittest.cc
similarity index 90%
rename from src/trace_processor/slice_tracker_unittest.cc
rename to src/trace_processor/importers/common/slice_tracker_unittest.cc
index 6b27258..35b1f5b 100644
--- a/src/trace_processor/slice_tracker_unittest.cc
+++ b/src/trace_processor/importers/common/slice_tracker_unittest.cc
@@ -16,10 +16,10 @@
 
 #include <vector>
 
-#include "src/trace_processor/args_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/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 #include "test/gtest_and_gmock.h"
 
 namespace perfetto {
@@ -65,7 +65,7 @@
   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.track_id()[0], track);
   EXPECT_EQ(slices.category()[0].raw_id(), 0u);
   EXPECT_EQ(slices.name()[0].raw_id(), 1u);
   EXPECT_EQ(slices.depth()[0], 0u);
@@ -98,7 +98,7 @@
   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.track_id()[0], track);
   EXPECT_EQ(slices.category()[0].raw_id(), 0u);
   EXPECT_EQ(slices.name()[0].raw_id(), 1u);
   EXPECT_EQ(slices.depth()[0], 0u);
@@ -135,14 +135,14 @@
   uint32_t idx = 0;
   EXPECT_EQ(slices.ts()[idx], 2);
   EXPECT_EQ(slices.dur()[idx], 8);
-  EXPECT_EQ(slices.track_id()[idx], track.value);
+  EXPECT_EQ(slices.track_id()[idx], track);
   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.track_id()[idx], track);
   EXPECT_EQ(slices.category()[idx].raw_id(), 0u);
   EXPECT_EQ(slices.name()[idx].raw_id(), 2u);
   EXPECT_EQ(slices.depth()[idx], 1u);
@@ -169,6 +169,25 @@
               ElementsAre(SliceInfo{0, 10}, SliceInfo{1, 8}, SliceInfo{2, 6}));
 }
 
+TEST(SliceTrackerTest, ParentId) {
+  TraceProcessorContext context;
+  context.storage.reset(new TraceStorage());
+  SliceTracker tracker(&context);
+
+  constexpr TrackId track{22u};
+  tracker.Begin(100, track, kNullStringId, kNullStringId);
+  tracker.Begin(101, track, kNullStringId, kNullStringId);
+  tracker.Begin(102, track, kNullStringId, kNullStringId);
+  tracker.End(103, track);
+  tracker.End(150, track);
+  tracker.End(200, track);
+
+  SliceId parent = context.storage->slice_table().id()[0];
+  SliceId child = context.storage->slice_table().id()[1];
+  EXPECT_THAT(context.storage->slice_table().parent_id().ToVectorForTesting(),
+              ElementsAre(base::nullopt, parent, child));
+}
+
 TEST(SliceTrackerTest, IgnoreMismatchedEnds) {
   TraceProcessorContext context;
   context.storage.reset(new TraceStorage());
@@ -228,9 +247,9 @@
   EXPECT_THAT(slices,
               ElementsAre(SliceInfo{0, 10}, SliceInfo{2, 6}, SliceInfo{3, 4}));
 
-  EXPECT_EQ(context.storage->slice_table().track_id()[0], track_a.value);
-  EXPECT_EQ(context.storage->slice_table().track_id()[1], track_b.value);
-  EXPECT_EQ(context.storage->slice_table().track_id()[2], track_b.value);
+  EXPECT_EQ(context.storage->slice_table().track_id()[0], track_a);
+  EXPECT_EQ(context.storage->slice_table().track_id()[1], track_b);
+  EXPECT_EQ(context.storage->slice_table().track_id()[2], track_b);
   EXPECT_EQ(context.storage->slice_table().depth()[0], 0u);
   EXPECT_EQ(context.storage->slice_table().depth()[1], 0u);
   EXPECT_EQ(context.storage->slice_table().depth()[2], 1u);
diff --git a/src/trace_processor/track_tracker.cc b/src/trace_processor/importers/common/track_tracker.cc
similarity index 69%
rename from src/trace_processor/track_tracker.cc
rename to src/trace_processor/importers/common/track_tracker.cc
index 94661b8..1950521 100644
--- a/src/trace_processor/track_tracker.cc
+++ b/src/trace_processor/importers/common/track_tracker.cc
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/track_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
 
-#include "src/trace_processor/args_tracker.h"
-#include "src/trace_processor/process_tracker.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -31,6 +31,8 @@
       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")),
+      category_key_(context->storage->InternString("category")),
       fuchsia_source_(context->storage->InternString("fuchsia")),
       chrome_source_(context->storage->InternString("chrome")),
       android_source_(context->storage->InternString("android")),
@@ -46,7 +48,7 @@
 
   tables::ThreadTrackTable::Row row;
   row.utid = utid;
-  auto id = context_->storage->mutable_thread_track_table()->Insert(row);
+  auto id = context_->storage->mutable_thread_track_table()->Insert(row).id;
   thread_tracks_[utid] = id;
   return id;
 }
@@ -58,7 +60,7 @@
 
   tables::ProcessTrackTable::Row row;
   row.upid = upid;
-  auto id = context_->storage->mutable_process_track_table()->Insert(row);
+  auto id = context_->storage->mutable_process_track_table()->Insert(row).id;
   process_tracks_[upid] = id;
   return id;
 }
@@ -70,7 +72,7 @@
     return it->second;
 
   tables::TrackTable::Row row(name);
-  auto id = context_->storage->mutable_track_table()->Insert(row);
+  auto id = context_->storage->mutable_track_table()->Insert(row).id;
   fuchsia_async_tracks_[correlation_id] = id;
 
   context_->args_tracker->AddArgsTo(id)
@@ -87,7 +89,7 @@
   if (it != gpu_tracks_.end())
     return it->second;
 
-  auto id = context_->storage->mutable_gpu_track_table()->Insert(row);
+  auto id = context_->storage->mutable_gpu_track_table()->Insert(row).id;
   gpu_tracks_[tuple] = id;
   return id;
 }
@@ -112,7 +114,8 @@
   // the ID's scope is global.
   tables::ProcessTrackTable::Row track(name);
   track.upid = upid;
-  TrackId id = context_->storage->mutable_process_track_table()->Insert(track);
+  TrackId id =
+      context_->storage->mutable_process_track_table()->Insert(track).id;
   chrome_tracks_[tuple] = id;
 
   context_->args_tracker->AddArgsTo(id)
@@ -136,7 +139,7 @@
 
   tables::ProcessTrackTable::Row row(name);
   row.upid = upid;
-  auto id = context_->storage->mutable_process_track_table()->Insert(row);
+  auto id = context_->storage->mutable_process_track_table()->Insert(row).id;
   android_async_tracks_[tuple] = id;
 
   context_->args_tracker->AddArgsTo(id)
@@ -146,6 +149,19 @@
   return id;
 }
 
+TrackId TrackTracker::InternPerfStackTrack(UniquePid upid) {
+  auto it = perf_stack_tracks_.find(upid);
+  if (it != perf_stack_tracks_.end())
+    return it->second;
+
+  StringId name = context_->storage->InternString("Stack samples");
+  tables::ProcessTrackTable::Row row(name);
+  row.upid = upid;
+  auto id = context_->storage->mutable_process_track_table()->Insert(row).id;
+  perf_stack_tracks_[upid] = id;
+  return id;
+}
+
 TrackId TrackTracker::InternLegacyChromeProcessInstantTrack(UniquePid upid) {
   auto it = chrome_process_instant_tracks_.find(upid);
   if (it != chrome_process_instant_tracks_.end())
@@ -153,7 +169,7 @@
 
   tables::ProcessTrackTable::Row row;
   row.upid = upid;
-  auto id = context_->storage->mutable_process_track_table()->Insert(row);
+  auto id = context_->storage->mutable_process_track_table()->Insert(row).id;
   chrome_process_instant_tracks_[upid] = id;
 
   context_->args_tracker->AddArgsTo(id).AddArg(
@@ -165,7 +181,7 @@
 TrackId TrackTracker::GetOrCreateLegacyChromeGlobalInstantTrack() {
   if (!chrome_global_instant_track_id_) {
     chrome_global_instant_track_id_ =
-        context_->storage->mutable_track_table()->Insert({});
+        context_->storage->mutable_track_table()->Insert({}).id;
 
     context_->args_tracker->AddArgsTo(*chrome_global_instant_track_id_)
         .AddArg(source_key_, Variadic::String(chrome_source_));
@@ -233,6 +249,38 @@
   it->second.min_timestamp = std::min(it->second.min_timestamp, timestamp);
 }
 
+void TrackTracker::ReserveDescriptorCounterTrack(uint64_t uuid,
+                                                 uint64_t parent_uuid,
+                                                 StringId category,
+                                                 int64_t unit_multiplier,
+                                                 bool is_incremental,
+                                                 uint32_t packet_sequence_id) {
+  DescriptorTrackReservation reservation;
+  reservation.parent_uuid = parent_uuid;
+  reservation.is_counter = true;
+  reservation.category = category;
+  reservation.unit_multiplier = unit_multiplier;
+  reservation.is_incremental = is_incremental;
+  // Incrementally encoded counters are only valid on a single sequence.
+  if (is_incremental)
+    reservation.packet_sequence_id = packet_sequence_id;
+
+  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;
+
+  // Counter 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 counter track with uuid %" PRIu64
+                " doesn't match earlier one",
+                uuid);
+  context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
+}
+
 void TrackTracker::ReserveDescriptorChildTrack(uint64_t uuid,
                                                uint64_t parent_uuid) {
   DescriptorTrackReservation reservation;
@@ -339,36 +387,75 @@
   base::Optional<TrackId> track_id;
   if (parent_track_id) {
     // If parent is a thread track, create another thread-associated track.
+    auto* thread_tracks = context_->storage->mutable_thread_track_table();
     base::Optional<uint32_t> thread_track_index =
-        context_->storage->thread_track_table().id().IndexOf(*parent_track_id);
+        thread_tracks->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);
+      if (reservation.is_counter) {
+        // Thread counter track.
+        auto* thread_counter_tracks =
+            context_->storage->mutable_thread_counter_track_table();
+        tables::ThreadCounterTrackTable::Row row;
+        row.utid = thread_tracks->utid()[*thread_track_index];
+        track_id = thread_counter_tracks->Insert(row).id;
+      } else {
+        // Thread slice track.
+        tables::ThreadTrackTable::Row row;
+        row.utid = thread_tracks->utid()[*thread_track_index];
+        track_id = thread_tracks->Insert(row).id;
+      }
     } else {
       // If parent is a process track, create another process-associated track.
+      auto* process_tracks = context_->storage->mutable_process_track_table();
       base::Optional<uint32_t> process_track_index =
-          context_->storage->process_track_table().id().IndexOf(
-              *parent_track_id);
+          process_tracks->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);
+        if (reservation.is_counter) {
+          // Process counter track.
+          auto* thread_counter_tracks =
+              context_->storage->mutable_process_counter_track_table();
+          tables::ProcessCounterTrackTable::Row row;
+          row.upid = process_tracks->upid()[*process_track_index];
+          track_id = thread_counter_tracks->Insert(row).id;
+        } else {
+          // Process slice track.
+          tables::ProcessTrackTable::Row row;
+          row.upid = process_tracks->upid()[*process_track_index];
+          track_id = process_tracks->Insert(row).id;
+        }
       }
     }
   }
 
   // Otherwise create a global track.
   if (!track_id) {
-    tables::TrackTable::Row track;
-    track_id = context_->storage->mutable_track_table()->Insert(track);
+    if (reservation.is_counter) {
+      // Global counter track.
+      tables::CounterTrackTable::Row row;
+      track_id =
+          context_->storage->mutable_counter_track_table()->Insert(row).id;
+    } else {
+      // Global slice track.
+      tables::TrackTable::Row row;
+      track_id = context_->storage->mutable_track_table()->Insert(row).id;
+    }
+    // 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();
   }
 
-  context_->args_tracker->AddArgsTo(*track_id)
-      .AddArg(source_key_, Variadic::String(descriptor_source_))
+  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));
+  }
+  if (reservation.category != kNullStringId) {
+    args.AddArg(category_key_, Variadic::String(reservation.category));
+  }
   return *track_id;
 }
 
@@ -390,6 +477,16 @@
   return *track_id;
 }
 
+TrackId TrackTracker::GetOrCreateTriggerTrack() {
+  if (trigger_track_id_) {
+    return *trigger_track_id_;
+  }
+  tables::TrackTable::Row row;
+  row.name = context_->storage->InternString("Trace Triggers");
+  trigger_track_id_ = context_->storage->mutable_track_table()->Insert(row).id;
+  return *trigger_track_id_;
+}
+
 TrackId TrackTracker::InternGlobalCounterTrack(StringId name) {
   auto it = global_counter_tracks_by_name_.find(name);
   if (it != global_counter_tracks_by_name_.end()) {
@@ -397,7 +494,8 @@
   }
 
   tables::CounterTrackTable::Row row(name);
-  TrackId track = context_->storage->mutable_counter_track_table()->Insert(row);
+  TrackId track =
+      context_->storage->mutable_counter_track_table()->Insert(row).id;
   global_counter_tracks_by_name_[name] = track;
   return track;
 }
@@ -412,7 +510,7 @@
   row.cpu = cpu;
 
   TrackId track =
-      context_->storage->mutable_cpu_counter_track_table()->Insert(row);
+      context_->storage->mutable_cpu_counter_track_table()->Insert(row).id;
   cpu_counter_tracks_[std::make_pair(name, cpu)] = track;
   return track;
 }
@@ -427,7 +525,7 @@
   row.utid = utid;
 
   TrackId track =
-      context_->storage->mutable_thread_counter_track_table()->Insert(row);
+      context_->storage->mutable_thread_counter_track_table()->Insert(row).id;
   utid_counter_tracks_[std::make_pair(name, utid)] = track;
   return track;
 }
@@ -442,7 +540,7 @@
   row.upid = upid;
 
   TrackId track =
-      context_->storage->mutable_process_counter_track_table()->Insert(row);
+      context_->storage->mutable_process_counter_track_table()->Insert(row).id;
   upid_counter_tracks_[std::make_pair(name, upid)] = track;
   return track;
 }
@@ -457,7 +555,7 @@
   row.irq = irq;
 
   TrackId track =
-      context_->storage->mutable_irq_counter_track_table()->Insert(row);
+      context_->storage->mutable_irq_counter_track_table()->Insert(row).id;
   irq_counter_tracks_[std::make_pair(name, irq)] = track;
   return track;
 }
@@ -473,7 +571,7 @@
   row.softirq = softirq;
 
   TrackId track =
-      context_->storage->mutable_softirq_counter_track_table()->Insert(row);
+      context_->storage->mutable_softirq_counter_track_table()->Insert(row).id;
   softirq_counter_tracks_[std::make_pair(name, softirq)] = track;
   return track;
 }
@@ -497,7 +595,63 @@
   row.description = description;
   row.unit = unit;
 
-  return context_->storage->mutable_gpu_counter_track_table()->Insert(row);
+  return context_->storage->mutable_gpu_counter_track_table()->Insert(row).id;
+}
+
+base::Optional<int64_t> TrackTracker::ConvertToAbsoluteCounterValue(
+    uint64_t counter_track_uuid,
+    uint32_t packet_sequence_id,
+    int64_t value) {
+  auto reservation_it = reserved_descriptor_tracks_.find(counter_track_uuid);
+  if (reservation_it == reserved_descriptor_tracks_.end()) {
+    PERFETTO_DLOG("Unknown counter track with uuid %" PRIu64,
+                  counter_track_uuid);
+    return base::nullopt;
+  }
+
+  DescriptorTrackReservation& reservation = reservation_it->second;
+  if (!reservation.is_counter) {
+    PERFETTO_DLOG("Track with uuid %" PRIu64 " is not a counter track",
+                  counter_track_uuid);
+    return base::nullopt;
+  }
+
+  if (reservation.unit_multiplier > 0)
+    value *= reservation.unit_multiplier;
+
+  if (reservation.is_incremental) {
+    if (reservation.packet_sequence_id != packet_sequence_id) {
+      PERFETTO_DLOG(
+          "Incremental counter track with uuid %" PRIu64
+          " was updated from the wrong packet sequence (expected: %" PRIu32
+          " got:%" PRIu32 ")",
+          counter_track_uuid, reservation.packet_sequence_id,
+          packet_sequence_id);
+      return base::nullopt;
+    }
+
+    reservation.latest_value += value;
+    value = reservation.latest_value;
+  }
+
+  return value;
+}
+
+void TrackTracker::OnIncrementalStateCleared(uint32_t packet_sequence_id) {
+  // TODO(eseckler): Improve on the runtime complexity of this. At O(hundreds)
+  // of packet sequences, incremental state clearing at O(trace second), and
+  // total number of tracks in O(thousands), a linear scan through all tracks
+  // here might not be fast enough.
+  for (auto& entry : reserved_descriptor_tracks_) {
+    DescriptorTrackReservation& reservation = entry.second;
+    // Only consider incremental counter tracks for current sequence.
+    if (!reservation.is_counter || !reservation.is_incremental ||
+        reservation.packet_sequence_id != packet_sequence_id) {
+      continue;
+    }
+    // Reset their value to 0, see CounterDescriptor's |is_incremental|.
+    reservation.latest_value = 0;
+  }
 }
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/track_tracker.h b/src/trace_processor/importers/common/track_tracker.h
similarity index 72%
rename from src/trace_processor/track_tracker.h
rename to src/trace_processor/importers/common/track_tracker.h
index de6233a..15c6955 100644
--- a/src/trace_processor/track_tracker.h
+++ b/src/trace_processor/importers/common/track_tracker.h
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_TRACK_TRACKER_H_
-#define SRC_TRACE_PROCESSOR_TRACK_TRACKER_H_
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_TRACK_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_TRACK_TRACKER_H_
 
-#include "src/trace_processor/trace_processor_context.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -52,6 +52,9 @@
                                   UniquePid upid,
                                   int64_t cookie);
 
+  // Interns a track for perf event stack samples, with process-wide grouping.
+  TrackId InternPerfStackTrack(UniquePid upid);
+
   // Interns a track for legacy Chrome process-scoped instant events into the
   // storage.
   TrackId InternLegacyChromeProcessInstantTrack(UniquePid upid);
@@ -96,6 +99,29 @@
   // track.
   void ReserveDescriptorChildTrack(uint64_t uuid, uint64_t parent_uuid);
 
+  // Associate a counter-type TrackDescriptor track identified by the given
+  // |uuid| with a parent track (usually a process or thread track). This is
+  // called during tokenization. If a reservation for the same |uuid| already
+  // exists, will attempt to update it. The provided |category| will be stored
+  // into the track's args.
+  //
+  // If |is_incremental| is true, the counter will only be valid on the packet
+  // sequence identified by |packet_sequence_id|. |unit_multiplier| is an
+  // optional multiplication factor applied to counter values. Values for the
+  // counter will be translated during tokenization via
+  // ConvertToAbsoluteCounterValue().
+  //
+  // 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 counter track for the same
+  // process/thread as its parent track.
+  void ReserveDescriptorCounterTrack(uint64_t uuid,
+                                     uint64_t parent_uuid,
+                                     StringId category,
+                                     int64_t unit_multiplier,
+                                     bool is_incremental,
+                                     uint32_t packet_sequence_id);
+
   // Returns the ID of the track for the TrackDescriptor with the given |uuid|.
   // This is called during parsing. The first call to GetDescriptorTrack() for
   // each |uuid| resolves and inserts the track (and its parent tracks,
@@ -107,6 +133,10 @@
   // Returns the ID of the implicit trace-global default TrackDescriptor track.
   TrackId GetOrCreateDefaultDescriptorTrack();
 
+  // Returns the ID of the implicit trace-global default track for triggers
+  // received by the service.
+  TrackId GetOrCreateTriggerTrack();
+
   // Interns a global counter track into the storage.
   TrackId InternGlobalCounterTrack(StringId name);
 
@@ -134,6 +164,21 @@
                                 StringId description = StringId::Null(),
                                 StringId unit = StringId::Null());
 
+  // Converts the given counter value to an absolute value in the unit of the
+  // counter, applying incremental delta encoding or unit multipliers as
+  // necessary. If the counter uses incremental encoding, |packet_sequence_id|
+  // must match the one in its track reservation. Returns base::nullopt if the
+  // counter track is unknown or an invalid |packet_sequence_id| was passed.
+  base::Optional<int64_t> ConvertToAbsoluteCounterValue(
+      uint64_t counter_track_uuid,
+      uint32_t packet_sequence_id,
+      int64_t value);
+
+  // Called by ProtoTraceTokenizer whenever incremental state is cleared on a
+  // packet sequence. Resets counter values for any incremental counters of
+  // the sequence identified by |packet_sequence_id|.
+  void OnIncrementalStateCleared(uint32_t packet_sequence_id);
+
  private:
   struct GpuTrackTuple {
     StringId track_name;
@@ -141,8 +186,8 @@
     int64_t context_id;
 
     friend bool operator<(const GpuTrackTuple& l, const GpuTrackTuple& r) {
-      return std::tie(l.track_name, l.scope, l.context_id)
-          < std::tie(r.track_name, r.scope, r.context_id);
+      return std::tie(l.track_name, l.scope, l.context_id) <
+             std::tie(r.track_name, r.scope, r.context_id);
     }
   };
   struct ChromeTrackTuple {
@@ -173,12 +218,23 @@
     base::Optional<uint32_t> tid;
     int64_t min_timestamp = 0;  // only set if |pid| and/or |tid| is set.
 
+    // For counter tracks.
+    bool is_counter = false;
+    StringId category = kNullStringId;
+    int64_t unit_multiplier = 1;
+    bool is_incremental = false;
+    uint32_t packet_sequence_id = 0;
+    int64_t latest_value = 0;
+
     // 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);
+      // Note that |min_timestamp| and |last_value| are ignored for this
+      // comparison.
+      return std::tie(parent_uuid, pid, tid, is_counter, category,
+                      unit_multiplier, is_incremental, packet_sequence_id) ==
+             std::tie(other.parent_uuid, pid, tid, is_counter, category,
+                      unit_multiplier, is_incremental, packet_sequence_id);
     }
   };
 
@@ -198,6 +254,7 @@
   std::map<uint64_t /* uuid */, DescriptorTrackReservation>
       reserved_descriptor_tracks_;
   std::map<uint64_t /* uuid */, TrackId> resolved_descriptor_tracks_;
+  std::map<UniquePid, TrackId> perf_stack_tracks_;
 
   std::map<StringId, TrackId> global_counter_tracks_by_name_;
   std::map<std::pair<StringId, uint32_t>, TrackId> cpu_counter_tracks_;
@@ -212,10 +269,14 @@
   std::map<UniquePid, uint64_t /*uuid*/> descriptor_uuids_by_upid_;
   std::map<UniqueTid, uint64_t /*uuid*/> descriptor_uuids_by_utid_;
 
+  base::Optional<TrackId> trigger_track_id_;
+
   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 category_key_ = kNullStringId;
 
   const StringId fuchsia_source_ = kNullStringId;
   const StringId chrome_source_ = kNullStringId;
@@ -230,4 +291,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_TRACK_TRACKER_H_
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_TRACK_TRACKER_H_
diff --git a/src/trace_processor/importers/default_modules.cc b/src/trace_processor/importers/default_modules.cc
new file mode 100644
index 0000000..2704478
--- /dev/null
+++ b/src/trace_processor/importers/default_modules.cc
@@ -0,0 +1,38 @@
+/*
+ * 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/default_modules.h"
+#include "src/trace_processor/importers/ftrace/ftrace_module.h"
+#include "src/trace_processor/importers/proto/profile_module.h"
+#include "src/trace_processor/importers/proto/proto_importer_module.h"
+#include "src/trace_processor/importers/proto/track_event_module.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+void RegisterDefaultModules(TraceProcessorContext* context) {
+  context->modules.emplace_back(new FtraceModule());
+  // Ftrace module is special, because it has one extra method for parsing
+  // ftrace packets. So we need to store a pointer to it separately.
+  context->ftrace_module =
+      static_cast<FtraceModule*>(context->modules.back().get());
+
+  context->modules.emplace_back(new TrackEventModule(context));
+  context->modules.emplace_back(new ProfileModule(context));
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/destructible.cc b/src/trace_processor/importers/default_modules.h
similarity index 65%
copy from src/trace_processor/destructible.cc
copy to src/trace_processor/importers/default_modules.h
index 22bcf6a..f1dcbf2 100644
--- a/src/trace_processor/destructible.cc
+++ b/src/trace_processor/importers/default_modules.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.
@@ -13,12 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include "src/trace_processor/destructible.h"
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_DEFAULT_MODULES_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_DEFAULT_MODULES_H_
+
+#include "src/trace_processor/types/trace_processor_context.h"
 
 namespace perfetto {
 namespace trace_processor {
 
-Destructible::~Destructible() = default;
+void RegisterDefaultModules(TraceProcessorContext*);
 
 }  // namespace trace_processor
 }  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_DEFAULT_MODULES_H_
diff --git a/src/trace_processor/importers/ftrace/binder_tracker.h b/src/trace_processor/importers/ftrace/binder_tracker.h
index 6b50fe2..e6e6ad5 100644
--- a/src/trace_processor/importers/ftrace/binder_tracker.h
+++ b/src/trace_processor/importers/ftrace/binder_tracker.h
@@ -19,7 +19,7 @@
 
 #include <stdint.h>
 
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/trace_storage.h"
 
 namespace perfetto {
 namespace trace_processor {
diff --git a/src/trace_processor/importers/ftrace/ftrace_descriptors.cc b/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
index e193077..ec65058 100644
--- a/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
@@ -24,7 +24,7 @@
 namespace trace_processor {
 namespace {
 
-std::array<MessageDescriptor, 334> descriptors{{
+std::array<MessageDescriptor, 335> descriptors{{
     {nullptr, 0, {}},
     {nullptr, 0, {}},
     {nullptr, 0, {}},
@@ -3357,11 +3357,13 @@
     },
     {
         "rss_stat",
-        2,
+        4,
         {
             {},
             {"member", ProtoSchemaType::kInt32},
             {"size", ProtoSchemaType::kInt64},
+            {"curr", ProtoSchemaType::kUint32},
+            {"mm_id", ProtoSchemaType::kUint32},
         },
     },
     {
@@ -3565,6 +3567,14 @@
             {"trace_begin", ProtoSchemaType::kUint32},
         },
     },
+    {
+        "mark_victim",
+        1,
+        {
+            {},
+            {"pid", ProtoSchemaType::kInt32},
+        },
+    },
 }};
 
 }  // namespace
@@ -3574,6 +3584,14 @@
   return &descriptors[id];
 }
 
+MessageDescriptor* GetMessageDescriptorForName(base::StringView name) {
+  for (MessageDescriptor& descriptor : descriptors) {
+    if (descriptor.name != nullptr && descriptor.name == name)
+      return &descriptor;
+  }
+  return nullptr;
+}
+
 size_t GetDescriptorsSize() {
   return descriptors.size();
 }
diff --git a/src/trace_processor/importers/ftrace/ftrace_descriptors.h b/src/trace_processor/importers/ftrace/ftrace_descriptors.h
index dc167ff..a8c965d 100644
--- a/src/trace_processor/importers/ftrace/ftrace_descriptors.h
+++ b/src/trace_processor/importers/ftrace/ftrace_descriptors.h
@@ -18,6 +18,8 @@
 #define SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_FTRACE_DESCRIPTORS_H_
 
 #include <array>
+
+#include "perfetto/ext/base/string_view.h"
 #include "perfetto/protozero/proto_utils.h"
 
 namespace perfetto {
@@ -47,6 +49,7 @@
 };
 
 MessageDescriptor* GetMessageDescriptorForId(size_t id);
+MessageDescriptor* GetMessageDescriptorForName(base::StringView name);
 size_t GetDescriptorsSize();
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.cc b/src/trace_processor/importers/ftrace/ftrace_parser.cc
index ac3d374..a1c3b89 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.cc
@@ -18,19 +18,20 @@
 
 #include "perfetto/base/logging.h"
 #include "perfetto/protozero/proto_decoder.h"
-#include "src/trace_processor/args_tracker.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
 #include "src/trace_processor/importers/ftrace/binder_tracker.h"
+#include "src/trace_processor/importers/syscalls/syscall_tracker.h"
 #include "src/trace_processor/importers/systrace/systrace_parser.h"
-#include "src/trace_processor/process_tracker.h"
-#include "src/trace_processor/stats.h"
-#include "src/trace_processor/syscall_tracker.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/stats.h"
+#include "src/trace_processor/storage/trace_storage.h"
 
 #include "protos/perfetto/trace/ftrace/binder.pbzero.h"
 #include "protos/perfetto/trace/ftrace/ftrace.pbzero.h"
 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
 #include "protos/perfetto/trace/ftrace/ftrace_stats.pbzero.h"
 #include "protos/perfetto/trace/ftrace/generic.pbzero.h"
+#include "protos/perfetto/trace/ftrace/ion.pbzero.h"
 #include "protos/perfetto/trace/ftrace/kmem.pbzero.h"
 #include "protos/perfetto/trace/ftrace/lowmemorykiller.pbzero.h"
 #include "protos/perfetto/trace/ftrace/mm_event.pbzero.h"
@@ -67,6 +68,8 @@
       cpu_freq_name_id_(context->storage->InternString("cpufreq")),
       gpu_freq_name_id_(context->storage->InternString("gpufreq")),
       cpu_idle_name_id_(context->storage->InternString("cpuidle")),
+      ion_total_id_(context->storage->InternString("mem.ion")),
+      ion_change_id_(context->storage->InternString("mem.ion_change")),
       ion_total_unknown_id_(context->storage->InternString("mem.ion.unknown")),
       ion_change_unknown_id_(
           context->storage->InternString("mem.ion_change.unknown")),
@@ -75,7 +78,8 @@
       oom_score_adj_id_(context->storage->InternString("oom_score_adj")),
       lmk_id_(context->storage->InternString("mem.lmk")),
       comm_name_id_(context->storage->InternString("comm")),
-      signal_name_id_(context_->storage->InternString("signal.sig")) {
+      signal_name_id_(context_->storage->InternString("signal.sig")),
+      oom_kill_id_(context_->storage->InternString("mem.oom_kill")) {
   // 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++) {
@@ -278,6 +282,10 @@
         ParseIonHeapGrowOrShrink(ts, pid, data, false);
         break;
       }
+      case FtraceEvent::kIonStatFieldNumber: {
+        ParseIonStat(ts, pid, data);
+        break;
+      }
       case FtraceEvent::kSignalGenerateFieldNumber: {
         ParseSignalGenerate(ts, data);
         break;
@@ -294,6 +302,10 @@
         ParseOOMScoreAdjUpdate(ts, data);
         break;
       }
+      case FtraceEvent::kMarkVictimFieldNumber: {
+        ParseOOMKill(ts, data);
+        break;
+      }
       case FtraceEvent::kMmEventRecordFieldNumber: {
         ParseMmEventRecord(ts, pid, data);
         break;
@@ -358,8 +370,9 @@
   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);
-  RawId id =
-      context_->storage->mutable_raw_table()->Insert({ts, event_id, cpu, utid});
+  RawId id = context_->storage->mutable_raw_table()
+                 ->Insert({ts, event_id, cpu, utid})
+                 .id;
   auto inserter = context_->args_tracker->AddArgsTo(id);
 
   for (auto it = evt.field(); it; ++it) {
@@ -396,8 +409,9 @@
   MessageDescriptor* m = GetMessageDescriptorForId(ftrace_id);
   const auto& message_strings = ftrace_message_strings_[ftrace_id];
   UniqueTid utid = context_->process_tracker->GetOrCreateThread(tid);
-  RawId id = context_->storage->mutable_raw_table()->Insert(
-      {ts, message_strings.message_name_id, cpu, utid});
+  RawId id = context_->storage->mutable_raw_table()
+                 ->Insert({ts, message_strings.message_name_id, cpu, utid})
+                 .id;
   auto inserter = context_->args_tracker->AddArgsTo(id);
 
   for (auto fld = decoder.ReadField(); fld.valid(); fld = decoder.ReadField()) {
@@ -545,6 +559,7 @@
       evt.trace_name(), tgid, evt.value());
 }
 
+/** Parses ion heap events present in Pixel kernels. */
 void FtraceParser::ParseIonHeapGrowOrShrink(int64_t ts,
                                             uint32_t pid,
                                             ConstBytes blob,
@@ -599,6 +614,24 @@
       "ION field mismatch");
 }
 
+/** Parses ion heap events (introduced in 4.19 kernels). */
+void FtraceParser::ParseIonStat(int64_t ts,
+                                uint32_t pid,
+                                protozero::ConstBytes data) {
+  protos::pbzero::IonStatFtraceEvent::Decoder ion(data.data, data.size);
+  // Push the global counter.
+  TrackId track =
+      context_->track_tracker->InternGlobalCounterTrack(ion_total_id_);
+  context_->event_tracker->PushCounter(ts, ion.total_allocated(), track);
+
+  // Push the change counter.
+  // TODO(b/121331269): these should really be instant events.
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  track =
+      context_->track_tracker->InternThreadCounterTrack(ion_change_id_, utid);
+  context_->event_tracker->PushCounter(ts, ion.len(), track);
+}
+
 // This event has both the pid of the thread that sent the signal and the
 // destination of the signal. Currently storing the pid of the destination.
 void FtraceParser::ParseSignalGenerate(int64_t ts, ConstBytes blob) {
@@ -661,6 +694,14 @@
                                                        oom_score_adj_id_, utid);
 }
 
+void FtraceParser::ParseOOMKill(int64_t ts, ConstBytes blob) {
+  protos::pbzero::MarkVictimFtraceEvent::Decoder evt(blob.data, blob.size);
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(
+      static_cast<uint32_t>(evt.pid()));
+  context_->event_tracker->PushInstant(ts, oom_kill_id_, utid,
+                                       RefType::kRefUtid, true);
+}
+
 void FtraceParser::ParseMmEventRecord(int64_t ts,
                                       uint32_t pid,
                                       ConstBytes blob) {
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.h b/src/trace_processor/importers/ftrace/ftrace_parser.h
index 10eacfe..539311b 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.h
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.h
@@ -18,14 +18,14 @@
 #define SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_FTRACE_PARSER_H_
 
 #include "perfetto/trace_processor/status.h"
-#include "src/trace_processor/event_tracker.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
 #include "src/trace_processor/importers/ftrace/binder_tracker.h"
 #include "src/trace_processor/importers/ftrace/ftrace_descriptors.h"
 #include "src/trace_processor/importers/ftrace/rss_stat_tracker.h"
 #include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
 #include "src/trace_processor/timestamped_trace_piece.h"
 #include "src/trace_processor/trace_blob_view.h"
-#include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -64,10 +64,12 @@
                                 uint32_t pid,
                                 protozero::ConstBytes,
                                 bool grow);
+  void ParseIonStat(int64_t ts, uint32_t pid, protozero::ConstBytes);
   void ParseSignalGenerate(int64_t ts, protozero::ConstBytes);
   void ParseSignalDeliver(int64_t ts, uint32_t pid, protozero::ConstBytes);
   void ParseLowmemoryKill(int64_t ts, protozero::ConstBytes);
   void ParseOOMScoreAdjUpdate(int64_t ts, protozero::ConstBytes);
+  void ParseOOMKill(int64_t ts, protozero::ConstBytes);
   void ParseMmEventRecord(int64_t ts, uint32_t pid, protozero::ConstBytes);
   void ParseSysEvent(int64_t ts,
                      uint32_t pid,
@@ -87,6 +89,8 @@
   const StringId cpu_freq_name_id_;
   const StringId gpu_freq_name_id_;
   const StringId cpu_idle_name_id_;
+  const StringId ion_total_id_;
+  const StringId ion_change_id_;
   const StringId ion_total_unknown_id_;
   const StringId ion_change_unknown_id_;
   const StringId signal_generate_id_;
@@ -95,6 +99,7 @@
   const StringId lmk_id_;
   const StringId comm_name_id_;
   const StringId signal_name_id_;
+  const StringId oom_kill_id_;
 
   struct FtraceMessageStrings {
     // The string id of name of the event field (e.g. sched_switch's id).
diff --git a/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc b/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
index 341eb1d..d196df2 100644
--- a/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
@@ -19,9 +19,9 @@
 #include "perfetto/base/logging.h"
 #include "perfetto/protozero/proto_decoder.h"
 #include "perfetto/protozero/proto_utils.h"
-#include "src/trace_processor/stats.h"
+#include "src/trace_processor/storage/stats.h"
+#include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/trace_sorter.h"
-#include "src/trace_processor/trace_storage.h"
 
 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
 #include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
diff --git a/src/trace_processor/importers/ftrace/ftrace_tokenizer.h b/src/trace_processor/importers/ftrace/ftrace_tokenizer.h
index 8d1007f..e48cb74 100644
--- a/src/trace_processor/importers/ftrace/ftrace_tokenizer.h
+++ b/src/trace_processor/importers/ftrace/ftrace_tokenizer.h
@@ -18,9 +18,9 @@
 #define SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_FTRACE_TOKENIZER_H_
 
 #include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
+#include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/trace_blob_view.h"
-#include "src/trace_processor/trace_processor_context.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 namespace perfetto {
 namespace trace_processor {
diff --git a/src/trace_processor/importers/ftrace/rss_stat_tracker.cc b/src/trace_processor/importers/ftrace/rss_stat_tracker.cc
index f536a02..f6f3548 100644
--- a/src/trace_processor/importers/ftrace/rss_stat_tracker.cc
+++ b/src/trace_processor/importers/ftrace/rss_stat_tracker.cc
@@ -16,9 +16,9 @@
 
 #include "src/trace_processor/importers/ftrace/rss_stat_tracker.h"
 
-#include "src/trace_processor/event_tracker.h"
-#include "src/trace_processor/process_tracker.h"
-#include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 #include "protos/perfetto/trace/ftrace/kmem.pbzero.h"
 
@@ -93,6 +93,14 @@
     return base::nullopt;
   }
 
+  // Verify that the utid in the map is still alive. This can happen if an mm
+  // struct we saw in the past is about to be reused after thread but we don't
+  // know the new process that struct will be associated with.
+  if (!context_->process_tracker->IsThreadAlive(it->second)) {
+    mm_id_to_utid_.erase(it);
+    return base::nullopt;
+  }
+
   // 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.
diff --git a/src/trace_processor/importers/ftrace/rss_stat_tracker.h b/src/trace_processor/importers/ftrace/rss_stat_tracker.h
index 7d297fb..50ad001 100644
--- a/src/trace_processor/importers/ftrace/rss_stat_tracker.h
+++ b/src/trace_processor/importers/ftrace/rss_stat_tracker.h
@@ -20,7 +20,7 @@
 #include <unordered_map>
 
 #include "perfetto/protozero/field.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/trace_storage.h"
 
 namespace perfetto {
 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 62fcec4..111c091 100644
--- a/src/trace_processor/importers/ftrace/sched_event_tracker.cc
+++ b/src/trace_processor/importers/ftrace/sched_event_tracker.cc
@@ -19,13 +19,13 @@
 #include <math.h>
 
 #include "perfetto/ext/base/utils.h"
-#include "src/trace_processor/args_tracker.h"
-#include "src/trace_processor/event_tracker.h"
-#include "src/trace_processor/ftrace_utils.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
 #include "src/trace_processor/importers/ftrace/ftrace_descriptors.h"
-#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/storage/stats.h"
+#include "src/trace_processor/types/task_state.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 #include "src/trace_processor/types/variadic.h"
 
 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
@@ -87,8 +87,8 @@
   // First use this data to close the previous slice.
   bool prev_pid_match_prev_next_pid = false;
   auto* pending_sched = &pending_sched_per_cpu_[cpu];
-  size_t pending_slice_idx = pending_sched->pending_slice_storage_idx;
-  if (pending_slice_idx < std::numeric_limits<size_t>::max()) {
+  uint32_t pending_slice_idx = pending_sched->pending_slice_storage_idx;
+  if (pending_slice_idx < std::numeric_limits<uint32_t>::max()) {
     prev_pid_match_prev_next_pid = prev_pid == pending_sched->last_pid;
     if (PERFETTO_LIKELY(prev_pid_match_prev_next_pid)) {
       ClosePendingSlice(pending_slice_idx, ts, prev_state);
@@ -154,8 +154,8 @@
 
   // Close the pending slice if any (we won't have one when processing the first
   // two compact events for a given cpu).
-  size_t pending_slice_idx = pending_sched->pending_slice_storage_idx;
-  if (pending_slice_idx < std::numeric_limits<size_t>::max())
+  uint32_t pending_slice_idx = pending_sched->pending_slice_storage_idx;
+  if (pending_slice_idx < std::numeric_limits<uint32_t>::max())
     ClosePendingSlice(pending_slice_idx, ts, prev_state);
 
   // Use the previous event's values to infer this event's "prev_*" fields.
@@ -181,22 +181,23 @@
 }
 
 PERFETTO_ALWAYS_INLINE
-size_t SchedEventTracker::AddRawEventAndStartSlice(uint32_t cpu,
-                                                   int64_t ts,
-                                                   UniqueTid prev_utid,
-                                                   uint32_t prev_pid,
-                                                   StringId prev_comm_id,
-                                                   int32_t prev_prio,
-                                                   int64_t prev_state,
-                                                   UniqueTid next_utid,
-                                                   uint32_t next_pid,
-                                                   StringId next_comm_id,
-                                                   int32_t next_prio) {
+uint32_t SchedEventTracker::AddRawEventAndStartSlice(uint32_t cpu,
+                                                     int64_t ts,
+                                                     UniqueTid prev_utid,
+                                                     uint32_t prev_pid,
+                                                     StringId prev_comm_id,
+                                                     int32_t prev_prio,
+                                                     int64_t prev_state,
+                                                     UniqueTid next_utid,
+                                                     uint32_t next_pid,
+                                                     StringId next_comm_id,
+                                                     int32_t next_prio) {
   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});
+    RawId id = context_->storage->mutable_raw_table()
+                   ->Insert({ts, sched_switch_id_, cpu, prev_utid})
+                   .id;
 
     // 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
@@ -219,19 +220,21 @@
 
   // Open a new scheduling slice, corresponding to the task that was
   // just switched to.
-  return context_->storage->mutable_slices()->AddSlice(
-      cpu, ts, 0 /* duration */, next_utid, ftrace_utils::TaskState(),
-      next_prio);
+  auto* sched = context_->storage->mutable_sched_slice_table();
+  auto row_and_id = sched->Insert(
+      {ts, 0 /* duration */, cpu, next_utid, kNullStringId, next_prio});
+  SchedId sched_id = row_and_id.id;
+  return *sched->id().IndexOf(sched_id);
 }
 
 PERFETTO_ALWAYS_INLINE
-void SchedEventTracker::ClosePendingSlice(size_t pending_slice_idx,
+void SchedEventTracker::ClosePendingSlice(uint32_t pending_slice_idx,
                                           int64_t ts,
                                           int64_t prev_state) {
-  auto* slices = context_->storage->mutable_slices();
+  auto* slices = context_->storage->mutable_sched_slice_table();
 
-  int64_t duration = ts - slices->start_ns()[pending_slice_idx];
-  slices->set_duration(pending_slice_idx, duration);
+  int64_t duration = ts - slices->ts()[pending_slice_idx];
+  slices->mutable_dur()->Set(pending_slice_idx, duration);
 
   // We store the state as a uint16 as we only consider values up to 2048
   // when unpacking the information inside; this allows savings of 48 bits
@@ -240,7 +243,10 @@
   if (!task_state.is_valid()) {
     context_->storage->IncrementStats(stats::task_state_invalid);
   }
-  slices->set_end_state(pending_slice_idx, task_state);
+  auto id = task_state.is_valid()
+                ? context_->storage->InternString(task_state.ToString().data())
+                : kNullStringId;
+  slices->mutable_end_state()->Set(pending_slice_idx, id);
 }
 
 // Processes a sched_waking that was decoded from a compact representation,
@@ -275,8 +281,9 @@
 
   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});
+    RawId id = context_->storage->mutable_raw_table()
+                   ->Insert({ts, sched_waking_id_, cpu, curr_utid})
+                   .id;
 
     // "success" is hardcoded as always 1 by the kernel, with a TODO to remove
     // it.
@@ -308,16 +315,18 @@
   // flush the sched events as they will probably be pushed in the next round
   // of ftrace events.
   int64_t end_ts = context_->storage->GetTraceTimestampBoundsNs().second;
-  auto* slices = context_->storage->mutable_slices();
+  auto* slices = context_->storage->mutable_sched_slice_table();
   for (const auto& pending_sched : pending_sched_per_cpu_) {
-    size_t row = pending_sched.pending_slice_storage_idx;
-    if (row == std::numeric_limits<size_t>::max())
+    uint32_t row = pending_sched.pending_slice_storage_idx;
+    if (row == std::numeric_limits<uint32_t>::max())
       continue;
 
-    int64_t duration = end_ts - slices->start_ns()[row];
-    slices->set_duration(row, duration);
-    slices->set_end_state(
-        row, ftrace_utils::TaskState(ftrace_utils::TaskState::kRunnable));
+    int64_t duration = end_ts - slices->ts()[row];
+    slices->mutable_dur()->Set(row, duration);
+
+    auto state = ftrace_utils::TaskState(ftrace_utils::TaskState::kRunnable);
+    auto id = context_->storage->InternString(state.ToString().data());
+    slices->mutable_end_state()->Set(row, id);
   }
 
   pending_sched_per_cpu_ = {};
diff --git a/src/trace_processor/importers/ftrace/sched_event_tracker.h b/src/trace_processor/importers/ftrace/sched_event_tracker.h
index 7a141ff..a4e8602 100644
--- a/src/trace_processor/importers/ftrace/sched_event_tracker.h
+++ b/src/trace_processor/importers/ftrace/sched_event_tracker.h
@@ -22,9 +22,9 @@
 
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/ext/base/utils.h"
-#include "src/trace_processor/destructible.h"
-#include "src/trace_processor/trace_processor_context.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/destructible.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -85,7 +85,7 @@
   // Information retained from the preceding sched_switch seen on a given cpu.
   struct PendingSchedInfo {
     // The pending scheduling slice that the next event will complete.
-    size_t pending_slice_storage_idx = std::numeric_limits<size_t>::max();
+    uint32_t pending_slice_storage_idx = std::numeric_limits<uint32_t>::max();
 
     // pid/utid/prio corresponding to the last sched_switch seen on this cpu
     // (its "next_*" fields). There is some duplication with respect to the
@@ -96,19 +96,19 @@
     int32_t last_prio = std::numeric_limits<int32_t>::max();
   };
 
-  size_t AddRawEventAndStartSlice(uint32_t cpu,
-                                  int64_t ts,
-                                  UniqueTid prev_utid,
-                                  uint32_t prev_pid,
-                                  StringId prev_comm_id,
-                                  int32_t prev_prio,
-                                  int64_t prev_state,
-                                  UniqueTid next_utid,
-                                  uint32_t next_pid,
-                                  StringId next_comm_id,
-                                  int32_t next_prio);
+  uint32_t AddRawEventAndStartSlice(uint32_t cpu,
+                                    int64_t ts,
+                                    UniqueTid prev_utid,
+                                    uint32_t prev_pid,
+                                    StringId prev_comm_id,
+                                    int32_t prev_prio,
+                                    int64_t prev_state,
+                                    UniqueTid next_utid,
+                                    uint32_t next_pid,
+                                    StringId next_comm_id,
+                                    int32_t next_prio);
 
-  void ClosePendingSlice(size_t slice_idx, int64_t ts, int64_t prev_state);
+  void ClosePendingSlice(uint32_t slice_idx, int64_t ts, int64_t prev_state);
 
   // Infromation retained from the preceding sched_switch seen on a given cpu.
   std::array<PendingSchedInfo, kMaxCpus> pending_sched_per_cpu_{};
diff --git a/src/trace_processor/importers/ftrace/sched_event_tracker_unittest.cc b/src/trace_processor/importers/ftrace/sched_event_tracker_unittest.cc
new file mode 100644
index 0000000..82363f9
--- /dev/null
+++ b/src/trace_processor/importers/ftrace/sched_event_tracker_unittest.cc
@@ -0,0 +1,139 @@
+/*
+ * 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/ftrace/sched_event_tracker.h"
+
+#include "perfetto/base/logging.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Invoke;
+
+class SchedEventTrackerTest : public ::testing::Test {
+ public:
+  SchedEventTrackerTest() {
+    context.storage.reset(new TraceStorage());
+    context.global_args_tracker.reset(new GlobalArgsTracker(&context));
+    context.args_tracker.reset(new ArgsTracker(&context));
+    context.event_tracker.reset(new EventTracker(&context));
+    context.process_tracker.reset(new ProcessTracker(&context));
+    sched_tracker = SchedEventTracker::GetOrCreate(&context);
+  }
+
+ protected:
+  TraceProcessorContext context;
+  SchedEventTracker* sched_tracker;
+};
+
+TEST_F(SchedEventTrackerTest, InsertSecondSched) {
+  uint32_t cpu = 3;
+  int64_t timestamp = 100;
+  uint32_t pid_1 = 2;
+  int64_t prev_state = 32;
+  static const char kCommProc1[] = "process1";
+  static const char kCommProc2[] = "process2";
+  uint32_t pid_2 = 4;
+  int32_t prio = 1024;
+
+  sched_tracker->PushSchedSwitch(cpu, timestamp, pid_1, kCommProc2, prio,
+                                 prev_state, pid_2, kCommProc1, prio);
+  ASSERT_EQ(context.storage->sched_slice_table().row_count(), 1ul);
+
+  sched_tracker->PushSchedSwitch(cpu, timestamp + 1, pid_2, kCommProc1, prio,
+                                 prev_state, pid_1, kCommProc2, prio);
+
+  ASSERT_EQ(context.storage->sched_slice_table().row_count(), 2ul);
+
+  const auto& timestamps = context.storage->sched_slice_table().ts();
+  ASSERT_EQ(timestamps[0], timestamp);
+  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->sched_slice_table().utid()[0], 1u);
+  ASSERT_EQ(context.storage->sched_slice_table().dur()[0], 1);
+}
+
+TEST_F(SchedEventTrackerTest, InsertThirdSched_SameThread) {
+  uint32_t cpu = 3;
+  int64_t timestamp = 100;
+  int64_t prev_state = 32;
+  static const char kCommProc1[] = "process1";
+  static const char kCommProc2[] = "process2";
+  int32_t prio = 1024;
+
+  sched_tracker->PushSchedSwitch(cpu, timestamp, /*tid=*/4, kCommProc2, prio,
+                                 prev_state,
+                                 /*tid=*/2, kCommProc1, prio);
+  ASSERT_EQ(context.storage->sched_slice_table().row_count(), 1u);
+
+  sched_tracker->PushSchedSwitch(cpu, timestamp + 1, /*tid=*/2, kCommProc1,
+                                 prio, prev_state,
+                                 /*tid=*/4, kCommProc2, prio);
+  sched_tracker->PushSchedSwitch(cpu, timestamp + 11, /*tid=*/4, kCommProc2,
+                                 prio, prev_state,
+                                 /*tid=*/2, kCommProc1, prio);
+  sched_tracker->PushSchedSwitch(cpu, timestamp + 31, /*tid=*/2, kCommProc1,
+                                 prio, prev_state,
+                                 /*tid=*/4, kCommProc2, prio);
+  ASSERT_EQ(context.storage->sched_slice_table().row_count(), 4ul);
+
+  const auto& timestamps = context.storage->sched_slice_table().ts();
+  ASSERT_EQ(timestamps[0], timestamp);
+  ASSERT_EQ(context.storage->thread_table().start_ts()[1], base::nullopt);
+  ASSERT_EQ(context.storage->sched_slice_table().dur()[0], 1u);
+  ASSERT_EQ(context.storage->sched_slice_table().dur()[1], 11u - 1u);
+  ASSERT_EQ(context.storage->sched_slice_table().dur()[2], 31u - 11u);
+  ASSERT_EQ(context.storage->sched_slice_table().utid()[0],
+            context.storage->sched_slice_table().utid()[2]);
+}
+
+TEST_F(SchedEventTrackerTest, UpdateThreadMatch) {
+  uint32_t cpu = 3;
+  int64_t timestamp = 100;
+  int64_t prev_state = 32;
+  static const char kCommProc1[] = "process1";
+  static const char kCommProc2[] = "process2";
+  int32_t prio = 1024;
+
+  sched_tracker->PushSchedSwitch(cpu, timestamp, /*tid=*/1, kCommProc2, prio,
+                                 prev_state,
+                                 /*tid=*/4, kCommProc1, prio);
+  sched_tracker->PushSchedSwitch(cpu, timestamp + 1, /*tid=*/4, kCommProc1,
+                                 prio, prev_state,
+                                 /*tid=*/1, kCommProc2, prio);
+
+  context.process_tracker->SetProcessMetadata(2, base::nullopt, "test");
+  context.process_tracker->UpdateThread(4, 2);
+
+  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);
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_record.h b/src/trace_processor/importers/fuchsia/fuchsia_record.h
index c1fb0ca..211785d 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_record.h
+++ b/src/trace_processor/importers/fuchsia/fuchsia_record.h
@@ -18,8 +18,8 @@
 #define SRC_TRACE_PROCESSOR_IMPORTERS_FUCHSIA_FUCHSIA_RECORD_H_
 
 #include "src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h"
+#include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/trace_blob_view.h"
-#include "src/trace_processor/trace_storage.h"
 
 #include <vector>
 
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc b/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc
index 46f0815..c1c5361 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc
+++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc
@@ -16,11 +16,11 @@
 
 #include "src/trace_processor/importers/fuchsia/fuchsia_trace_parser.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/track_tracker.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
 
 namespace perfetto {
 namespace trace_processor {
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc b/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc
index badef38..ffc16eb 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc
+++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc
@@ -21,12 +21,12 @@
 
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/string_view.h"
-#include "src/trace_processor/ftrace_utils.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
 #include "src/trace_processor/importers/fuchsia/fuchsia_record.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_sorter.h"
+#include "src/trace_processor/types/task_state.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -542,9 +542,13 @@
           }
         }
 
-        storage->mutable_slices()->AddSlice(cpu, previous_thread.start_ts,
-                                            ts - previous_thread.start_ts, utid,
-                                            end_state, outgoing_priority);
+        auto id =
+            end_state.is_valid()
+                ? context_->storage->InternString(end_state.ToString().data())
+                : kNullStringId;
+        storage->mutable_sched_slice_table()->Insert(
+            {previous_thread.start_ts, ts - previous_thread.start_ts, cpu, utid,
+             id, outgoing_priority});
       }
 
       RunningThread new_running;
@@ -568,5 +572,7 @@
   providers_[provider_id] = std::move(provider);
 }
 
+void FuchsiaTraceTokenizer::NotifyEndOfFile() {}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h b/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h
index e5dd9af..efec186 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h
+++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h
@@ -19,8 +19,8 @@
 
 #include "src/trace_processor/chunked_trace_reader.h"
 #include "src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h"
+#include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/trace_blob_view.h"
-#include "src/trace_processor/trace_storage.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -36,6 +36,7 @@
 
   // ChunkedTraceReader implementation
   util::Status Parse(std::unique_ptr<uint8_t[]>, size_t) override;
+  void NotifyEndOfFile() override;
 
  private:
   struct ProviderInfo {
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h b/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h
index 2ccc7a6..3af9649 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h
+++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h
@@ -22,8 +22,8 @@
 #include <functional>
 
 #include "perfetto/ext/base/string_view.h"
+#include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/trace_blob_view.h"
-#include "src/trace_processor/trace_storage.h"
 
 namespace perfetto {
 namespace trace_processor {
diff --git a/src/trace_processor/gzip_trace_parser.cc b/src/trace_processor/importers/gzip/gzip_trace_parser.cc
similarity index 60%
rename from src/trace_processor/gzip_trace_parser.cc
rename to src/trace_processor/importers/gzip/gzip_trace_parser.cc
index 4dfce0b..89e8c1d 100644
--- a/src/trace_processor/gzip_trace_parser.cc
+++ b/src/trace_processor/importers/gzip/gzip_trace_parser.cc
@@ -14,12 +14,10 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/gzip_trace_parser.h"
+#include "src/trace_processor/importers/gzip/gzip_trace_parser.h"
 
 #include <string>
 
-#include <zlib.h>
-
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/string_view.h"
@@ -29,17 +27,9 @@
 namespace trace_processor {
 
 GzipTraceParser::GzipTraceParser(TraceProcessorContext* context)
-    : context_(context), z_stream_(new z_stream()) {
-  z_stream_->zalloc = Z_NULL;
-  z_stream_->zfree = Z_NULL;
-  z_stream_->opaque = Z_NULL;
-  inflateInit2(z_stream_.get(), 32 + 15);
-}
+    : context_(context) {}
 
-GzipTraceParser::~GzipTraceParser() {
-  // Ensure the call to inflateEnd to prevent leaks of internal state.
-  inflateEnd(z_stream_.get());
-}
+GzipTraceParser::~GzipTraceParser() = default;
 
 util::Status GzipTraceParser::Parse(std::unique_ptr<uint8_t[]> data,
                                     size_t size) {
@@ -60,41 +50,32 @@
       len -= strlen(kSystraceFileHeader) + offset;
     }
   }
-
-  z_stream_->next_in = start;
-  z_stream_->avail_in = static_cast<uInt>(len);
+  decompressor_.SetInput(start, len);
 
   // Our default uncompressed buffer size is 32MB as it allows for good
   // throughput.
+  using ResultCode = GzipDecompressor::ResultCode;
   constexpr size_t kUncompressedBufferSize = 32 * 1024 * 1024;
-  int ret = Z_OK;
-  for (; ret != Z_STREAM_END && z_stream_->avail_in != 0;) {
+
+  for (auto ret = ResultCode::kOk; ret != ResultCode::kEof;) {
     std::unique_ptr<uint8_t[]> buffer(new uint8_t[kUncompressedBufferSize]);
-    z_stream_->next_out = buffer.get();
-    z_stream_->avail_out = static_cast<uInt>(kUncompressedBufferSize);
+    auto result =
+        decompressor_.Decompress(buffer.get(), kUncompressedBufferSize);
+    ret = result.ret;
+    if (ret == ResultCode::kError || ret == ResultCode::kNoProgress)
+      return util::ErrStatus("Unable to decompress gzip/ctrace trace");
+    if (ret == ResultCode::kNeedsMoreInput)
+      break;
 
-    ret = inflate(z_stream_.get(), Z_NO_FLUSH);
-    switch (ret) {
-      case Z_NEED_DICT:
-      case Z_DATA_ERROR:
-      case Z_MEM_ERROR:
-        // Ignore inflateEnd error as we will error out anyway.
-        inflateEnd(z_stream_.get());
-        return util::ErrStatus("Error decompressing ctrace file");
-    }
-
-    size_t read = kUncompressedBufferSize - z_stream_->avail_out;
-    util::Status status = inner_->Parse(std::move(buffer), read);
+    util::Status status =
+        inner_->Parse(std::move(buffer), result.bytes_written);
     if (!status.ok())
       return status;
   }
-  if (ret == Z_STREAM_END) {
-    ret = inflateEnd(z_stream_.get());
-    if (ret == Z_STREAM_ERROR)
-      return util::ErrStatus("Error finishing decompression");
-  }
   return util::OkStatus();
 }
 
+void GzipTraceParser::NotifyEndOfFile() {}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/gzip_trace_parser.h b/src/trace_processor/importers/gzip/gzip_trace_parser.h
similarity index 78%
rename from src/trace_processor/gzip_trace_parser.h
rename to src/trace_processor/importers/gzip/gzip_trace_parser.h
index a129c4b..c83416e 100644
--- a/src/trace_processor/gzip_trace_parser.h
+++ b/src/trace_processor/importers/gzip/gzip_trace_parser.h
@@ -14,12 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_GZIP_TRACE_PARSER_H_
-#define SRC_TRACE_PROCESSOR_GZIP_TRACE_PARSER_H_
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_GZIP_GZIP_TRACE_PARSER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_GZIP_GZIP_TRACE_PARSER_H_
 
 #include "src/trace_processor/chunked_trace_reader.h"
-
-struct z_stream_s;
+#include "src/trace_processor/importers/gzip/gzip_utils.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -33,14 +32,15 @@
 
   // ChunkedTraceReader implementation
   util::Status Parse(std::unique_ptr<uint8_t[]>, size_t) override;
+  void NotifyEndOfFile() override;
 
  private:
   TraceProcessorContext* const context_;
-  std::unique_ptr<z_stream_s> z_stream_;
+  GzipDecompressor decompressor_;
   std::unique_ptr<ChunkedTraceReader> inner_;
 };
 
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_GZIP_TRACE_PARSER_H_
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_GZIP_GZIP_TRACE_PARSER_H_
diff --git a/src/trace_processor/importers/gzip/gzip_utils.cc b/src/trace_processor/importers/gzip/gzip_utils.cc
new file mode 100644
index 0000000..1edb9d0
--- /dev/null
+++ b/src/trace_processor/importers/gzip/gzip_utils.cc
@@ -0,0 +1,106 @@
+/*
+ * 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/gzip/gzip_utils.h"
+
+// For bazel build.
+#include "perfetto/base/build_config.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+#include <zlib.h>
+#else
+struct z_stream_s {};
+#endif
+
+namespace perfetto {
+namespace trace_processor {
+namespace gzip {
+
+bool IsGzipSupported() {
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+  return true;
+#else
+  return false;
+#endif
+}
+
+}  // namespace gzip
+
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+GzipDecompressor::GzipDecompressor() : z_stream_(new z_stream()) {
+  z_stream_->zalloc = Z_NULL;
+  z_stream_->zfree = Z_NULL;
+  z_stream_->opaque = Z_NULL;
+  inflateInit2(z_stream_.get(), 32 + 15);
+}
+#else
+GzipDecompressor::GzipDecompressor() = default;
+#endif
+
+GzipDecompressor::~GzipDecompressor() {
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+  // Ensure the call to inflateEnd to prevent leaks of internal state.
+  inflateEnd(z_stream_.get());
+#endif
+}
+
+void GzipDecompressor::Reset() {
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+  inflateReset(z_stream_.get());
+#endif
+}
+
+void GzipDecompressor::SetInput(const uint8_t* data, size_t size) {
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+  // This const_cast is not harmfull as zlib will not modify the data in this
+  // pointer. This is only necessary because of the build flags we use to be
+  // compatible with other embedders.
+  z_stream_->next_in = const_cast<uint8_t*>(data);
+  z_stream_->avail_in = static_cast<uInt>(size);
+#endif
+}
+
+GzipDecompressor::Result GzipDecompressor::Decompress(uint8_t* out,
+                                                      size_t out_size) {
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+  if (z_stream_->avail_in == 0)
+    return Result{ResultCode::kNeedsMoreInput, 0};
+
+  z_stream_->next_out = out;
+  z_stream_->avail_out = static_cast<uInt>(out_size);
+
+  int ret = inflate(z_stream_.get(), Z_NO_FLUSH);
+  switch (ret) {
+    case Z_NEED_DICT:
+    case Z_DATA_ERROR:
+    case Z_MEM_ERROR:
+      // Ignore inflateEnd error as we will error out anyway.
+      inflateEnd(z_stream_.get());
+      return Result{ResultCode::kError, 0};
+    case Z_STREAM_END:
+      return Result{ResultCode::kEof, out_size - z_stream_->avail_out};
+    case Z_BUF_ERROR:
+      return Result{ResultCode::kNoProgress, 0};
+    default:
+      return Result{ResultCode::kOk, out_size - z_stream_->avail_out};
+  }
+#else
+  return Result{ResultCode::kError, 0};
+#endif
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/gzip/gzip_utils.h b/src/trace_processor/importers/gzip/gzip_utils.h
new file mode 100644
index 0000000..624363f
--- /dev/null
+++ b/src/trace_processor/importers/gzip/gzip_utils.h
@@ -0,0 +1,72 @@
+/*
+ * 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_IMPORTERS_GZIP_GZIP_UTILS_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_GZIP_GZIP_UTILS_H_
+
+#include <memory>
+
+struct z_stream_s;
+
+namespace perfetto {
+namespace trace_processor {
+
+namespace gzip {
+
+// Returns whether gzip related functioanlity is supported with the current
+// build flags.
+bool IsGzipSupported();
+
+}  // namespace gzip
+
+class GzipDecompressor {
+ public:
+  enum class ResultCode {
+    kOk,
+    kEof,
+    kError,
+    kNoProgress,
+    kNeedsMoreInput,
+  };
+  struct Result {
+    // The return code of the decompression.
+    ResultCode ret;
+
+    // The amount of bytes written to output.
+    // Only valid if |ResultCode::kOk|.
+    size_t bytes_written;
+  };
+
+  GzipDecompressor();
+  ~GzipDecompressor();
+
+  // Sets the input pointer and size of the gzip stream to inflate.
+  void SetInput(const uint8_t* data, size_t size);
+
+  // Decompresses the input previously provided in |SetInput|.
+  Result Decompress(uint8_t* out, size_t out_size);
+
+  // Sets the state of the decompressor to reuse with other gzip streams.
+  void Reset();
+
+ private:
+  std::unique_ptr<z_stream_s> z_stream_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_GZIP_GZIP_UTILS_H_
diff --git a/src/trace_processor/importers/json/json_trace_parser.cc b/src/trace_processor/importers/json/json_trace_parser.cc
index 7a59c63..37b6820 100644
--- a/src/trace_processor/importers/json/json_trace_parser.cc
+++ b/src/trace_processor/importers/json/json_trace_parser.cc
@@ -14,15 +14,9 @@
  * limitations under the License.
  */
 
-// For bazel build.
-#include "perfetto/base/build_config.h"
-#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON_IMPORT)
-
 #include "src/trace_processor/importers/json/json_trace_parser.h"
 
 #include <inttypes.h>
-#include <json/reader.h>
-#include <json/value.h>
 
 #include <limits>
 #include <string>
@@ -30,17 +24,18 @@
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/ext/base/utils.h"
-#include "src/trace_processor/importers/json/json_trace_utils.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/track_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/importers/json/json_tracker.h"
+#include "src/trace_processor/importers/json/json_utils.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 namespace perfetto {
 namespace trace_processor {
 
 JsonTraceParser::JsonTraceParser(TraceProcessorContext* context)
-    : context_(context) {}
+    : context_(context), systrace_line_parser_(context) {}
 
 JsonTraceParser::~JsonTraceParser() = default;
 
@@ -52,7 +47,16 @@
 
 void JsonTraceParser::ParseTracePacket(int64_t timestamp,
                                        TimestampedTracePiece ttp) {
-  PERFETTO_DCHECK(ttp.json_value != nullptr);
+  PERFETTO_DCHECK(json::IsJsonSupported());
+
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
+  PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kJsonValue ||
+                  ttp.type == TimestampedTracePiece::Type::kSystraceLine);
+  if (ttp.type == TimestampedTracePiece::Type::kSystraceLine) {
+    systrace_line_parser_.ParseLine(*ttp.systrace_line);
+    return;
+  }
+
   const Json::Value& value = *(ttp.json_value);
 
   ProcessTracker* procs = context_->process_tracker.get();
@@ -68,9 +72,9 @@
   base::Optional<uint32_t> opt_tid;
 
   if (value.isMember("pid"))
-    opt_pid = json_trace_utils::CoerceToUint32(value["pid"]);
+    opt_pid = json::CoerceToUint32(value["pid"]);
   if (value.isMember("tid"))
-    opt_tid = json_trace_utils::CoerceToUint32(value["tid"]);
+    opt_tid = json::CoerceToUint32(value["tid"]);
 
   uint32_t pid = opt_pid.value_or(0);
   uint32_t tid = opt_tid.value_or(pid);
@@ -86,25 +90,32 @@
   StringId name_id = storage->InternString(name);
   UniqueTid utid = procs->UpdateThread(tid, pid);
 
+  auto args_inserter = [this, &value](ArgsTracker::BoundInserter* inserter) {
+    if (value.isMember("args")) {
+      json::AddJsonValueToArgs(value["args"], /* flat_key = */ "args",
+                               /* key = */ "args", context_->storage.get(),
+                               inserter);
+    }
+  };
   switch (phase) {
     case 'B': {  // TRACE_EVENT_BEGIN.
       TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
-      slice_tracker->Begin(timestamp, track_id, cat_id, name_id);
+      slice_tracker->Begin(timestamp, track_id, cat_id, name_id, args_inserter);
       break;
     }
     case 'E': {  // TRACE_EVENT_END.
       TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
-      slice_tracker->End(timestamp, track_id, cat_id, name_id);
+      slice_tracker->End(timestamp, track_id, cat_id, name_id, args_inserter);
       break;
     }
     case 'X': {  // TRACE_EVENT (scoped event).
       base::Optional<int64_t> opt_dur =
-          json_trace_utils::CoerceToNs(value["dur"]);
+          JsonTracker::GetOrCreate(context_)->CoerceToTs(value["dur"]);
       if (!opt_dur.has_value())
         return;
       TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
       slice_tracker->Scoped(timestamp, track_id, cat_id, name_id,
-                            opt_dur.value());
+                            opt_dur.value(), args_inserter);
       break;
     }
     case 'M': {  // Metadata events (process and thread names).
@@ -123,9 +134,14 @@
       }
     }
   }
+#else
+  perfetto::base::ignore_result(timestamp);
+  perfetto::base::ignore_result(ttp);
+  perfetto::base::ignore_result(context_);
+  PERFETTO_ELOG("Cannot parse JSON trace due to missing JSON support");
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
 }
 
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_TP_JSON_IMPORT)
diff --git a/src/trace_processor/importers/json/json_trace_parser.h b/src/trace_processor/importers/json/json_trace_parser.h
index 30941bf..b2f7cc4 100644
--- a/src/trace_processor/importers/json/json_trace_parser.h
+++ b/src/trace_processor/importers/json/json_trace_parser.h
@@ -23,9 +23,10 @@
 #include <tuple>
 #include <unordered_map>
 
+#include "src/trace_processor/importers/json/json_tracker.h"
+#include "src/trace_processor/importers/systrace/systrace_line_parser.h"
 #include "src/trace_processor/timestamped_trace_piece.h"
 #include "src/trace_processor/trace_parser.h"
-#include "src/trace_processor/trace_storage.h"
 
 namespace Json {
 class Value;
@@ -36,9 +37,6 @@
 
 class TraceProcessorContext;
 
-base::Optional<int64_t> CoerceToInt64(const Json::Value& value);
-base::Optional<uint32_t> CoerceToUint32(const Json::Value& value);
-
 // Parses legacy chrome JSON traces. The support for now is extremely rough
 // and supports only explicit TRACE_EVENT_BEGIN/END events.
 class JsonTraceParser : public TraceParser {
@@ -52,6 +50,7 @@
 
  private:
   TraceProcessorContext* const context_;
+  SystraceLineParser systrace_line_parser_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/json/json_trace_tokenizer.cc b/src/trace_processor/importers/json/json_trace_tokenizer.cc
index 0eac6df..329d05f 100644
--- a/src/trace_processor/importers/json/json_trace_tokenizer.cc
+++ b/src/trace_processor/importers/json/json_trace_tokenizer.cc
@@ -14,28 +14,96 @@
  * limitations under the License.
  */
 
-// For bazel build.
-#include "perfetto/base/build_config.h"
-#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON_IMPORT)
 
 #include "src/trace_processor/importers/json/json_trace_tokenizer.h"
 
-#include <json/reader.h>
-#include <json/value.h>
+#include "perfetto/base/build_config.h"
+#include "perfetto/ext/base/string_utils.h"
 
-#include "src/trace_processor/importers/json/json_trace_utils.h"
-#include "src/trace_processor/stats.h"
+#include "src/trace_processor/importers/json/json_tracker.h"
+#include "src/trace_processor/importers/json/json_utils.h"
+#include "src/trace_processor/storage/stats.h"
 #include "src/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/trace_sorter.h"
 
 namespace perfetto {
 namespace trace_processor {
 
-// Parses at most one JSON dictionary and returns a pointer to the end of it,
-// or nullptr if no dict could be detected.
-// This is to avoid decoding the full trace in memory and reduce heap traffic.
-// E.g.  input:  { a:1 b:{ c:2, d:{ e:3 } } } , { a:4, ... },
-//       output: [   only this is parsed    ] ^return value points here.
+namespace {
+
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
+util::Status AppendUnescapedCharacter(char c,
+                                      bool is_escaping,
+                                      std::string* key) {
+  if (is_escaping) {
+    switch (c) {
+      case '"':
+      case '\\':
+      case '/':
+        key->push_back(c);
+        break;
+      case 'b':
+        key->push_back('\b');
+        break;
+      case 'f':
+        key->push_back('\f');
+        break;
+      case 'n':
+        key->push_back('\n');
+        break;
+      case 'r':
+        key->push_back('\r');
+        break;
+      case 't':
+        key->push_back('\t');
+        break;
+      default:
+        // We don't support any other escape sequences (concretely \uxxxx
+        // which JSON supports but is too much effort for us to parse).
+        return util::ErrStatus("Illegal character in JSON");
+    }
+  } else if (c != '\\') {
+    key->push_back(c);
+  }
+  return util::OkStatus();
+}
+
+enum class ReadStringRes {
+  kEndOfString,
+  kNeedsMoreData,
+  kFatalError,
+};
+ReadStringRes ReadOneJsonString(const char* start,
+                                const char* end,
+                                std::string* key,
+                                const char** next) {
+  bool is_escaping = false;
+  for (const char* s = start; s < end; s++) {
+    // Control characters are not allowed in JSON strings.
+    if (iscntrl(*s))
+      return ReadStringRes::kFatalError;
+
+    // If we get a quote character end of the string.
+    if (*s == '"' && !is_escaping) {
+      *next = s + 1;
+      return ReadStringRes::kEndOfString;
+    }
+
+    util::Status status = AppendUnescapedCharacter(*s, is_escaping, key);
+    if (!status.ok())
+      return ReadStringRes::kFatalError;
+
+    // If we're in a string and we see a backslash and the last character was
+    // not a backslash the next character is escaped:
+    is_escaping = *s == '\\' && !is_escaping;
+  }
+  return ReadStringRes::kNeedsMoreData;
+}
+#endif
+
+}  // namespace
+
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
 ReadDictRes ReadOneJsonDict(const char* start,
                             const char* end,
                             Json::Value* value,
@@ -68,17 +136,18 @@
     }
     if (*s == '}') {
       if (braces <= 0)
-        return kEndOfTrace;
+        return ReadDictRes::kEndOfTrace;
       if (--braces > 0)
         continue;
-      Json::Reader reader;
-      if (!reader.parse(dict_begin, s + 1, *value, /*collectComments=*/false)) {
-        PERFETTO_ELOG("JSON error: %s",
-                      reader.getFormattedErrorMessages().c_str());
-        return kFatalError;
+      size_t len = static_cast<size_t>((s + 1) - dict_begin);
+      auto opt_value = json::ParseJsonString(base::StringView(dict_begin, len));
+      if (!opt_value) {
+        PERFETTO_ELOG("Error while parsing JSON string during tokenization");
+        return ReadDictRes::kFatalError;
       }
+      *value = std::move(*opt_value);
       *next = s + 1;
-      return kFoundDict;
+      return ReadDictRes::kFoundDict;
     }
     if (*s == '[') {
       square_brackets++;
@@ -89,67 +158,332 @@
         // We've reached the end of [traceEvents] array.
         // There might be other top level keys in the json (e.g. metadata)
         // after.
-        // TODO(dproy): Handle trace metadata importing.
-        return kEndOfTrace;
+        *next = s + 1;
+        return ReadDictRes::kEndOfArray;
       }
       square_brackets--;
     }
   }
-  return kNeedsMoreData;
+  return ReadDictRes::kNeedsMoreData;
 }
 
+ReadKeyRes ReadOneJsonKey(const char* start,
+                          const char* end,
+                          std::string* key,
+                          const char** next) {
+  enum class NextToken {
+    kStringOrEndOfDict,
+    kColon,
+    kValue,
+  };
+
+  NextToken next_token = NextToken::kStringOrEndOfDict;
+  bool seen_comma = false;
+  for (const char* s = start; s < end; s++) {
+    // Whitespace characters anywhere can be skipped.
+    if (isspace(*s))
+      continue;
+
+    switch (next_token) {
+      case NextToken::kStringOrEndOfDict: {
+        // If we see a closing brace, that means we've reached the end of the
+        // wrapping dictionary.
+        if (*s == '}') {
+          *next = s + 1;
+          return ReadKeyRes::kEndOfDictionary;
+        }
+
+        // If we see a comma separator, just ignore it.
+        if (*s == ',') {
+          if (!seen_comma)
+            continue;
+
+          seen_comma = true;
+          continue;
+        }
+
+        // If we see anything else but a quote character here, this cannot be a
+        // valid key.
+        if (*s != '"')
+          return ReadKeyRes::kFatalError;
+
+        auto res = ReadOneJsonString(s + 1, end, key, &s);
+        if (res == ReadStringRes::kFatalError)
+          return ReadKeyRes::kFatalError;
+        if (res == ReadStringRes::kNeedsMoreData)
+          return ReadKeyRes::kNeedsMoreData;
+
+        // We need to decrement from the pointer as the loop will increment
+        // it back up.
+        s--;
+        next_token = NextToken::kColon;
+        break;
+      }
+      case NextToken::kColon:
+        if (*s != ':')
+          return ReadKeyRes::kFatalError;
+        next_token = NextToken::kValue;
+        break;
+      case NextToken::kValue:
+        *next = s;
+        return ReadKeyRes::kFoundKey;
+    }
+  }
+  return ReadKeyRes::kNeedsMoreData;
+}
+
+ReadSystemLineRes ReadOneSystemTraceLine(const char* start,
+                                         const char* end,
+                                         std::string* line,
+                                         const char** next) {
+  bool is_escaping = false;
+  for (const char* s = start; s < end; s++) {
+    // If we get a quote character and we're not escaping, we are done with the
+    // system trace string.
+    if (*s == '"' && !is_escaping) {
+      *next = s + 1;
+      return ReadSystemLineRes::kEndOfSystemTrace;
+    }
+
+    // If we are escaping n, that means this is a new line which is a delimiter
+    // for a system trace line.
+    if (*s == 'n' && is_escaping) {
+      *next = s + 1;
+      return ReadSystemLineRes::kFoundLine;
+    }
+
+    util::Status status = AppendUnescapedCharacter(*s, is_escaping, line);
+    if (!status.ok())
+      return ReadSystemLineRes::kFatalError;
+
+    // If we're in a string and we see a backslash and the last character was
+    // not a backslash the next character is escaped:
+    is_escaping = *s == '\\' && !is_escaping;
+  }
+  return ReadSystemLineRes::kNeedsMoreData;
+}
+#endif
+
 JsonTraceTokenizer::JsonTraceTokenizer(TraceProcessorContext* ctx)
     : context_(ctx) {}
 JsonTraceTokenizer::~JsonTraceTokenizer() = default;
 
 util::Status JsonTraceTokenizer::Parse(std::unique_ptr<uint8_t[]> data,
                                        size_t size) {
+  PERFETTO_DCHECK(json::IsJsonSupported());
+
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
   buffer_.insert(buffer_.end(), data.get(), data.get() + size);
   const char* buf = buffer_.data();
   const char* next = buf;
   const char* end = buf + buffer_.size();
 
+  JsonTracker* json_tracker = JsonTracker::GetOrCreate(context_);
+
+  // It's possible the displayTimeUnit key is at the end of the json
+  // file so to be correct we ought to parse the whole file looking
+  // for this key before parsing any events however this would require
+  // two passes on the file so for now we only handle displayTimeUnit
+  // correctly if it is at the beginning of the file.
+  const base::StringView view(buf, size);
+  if (view.find("\"displayTimeUnit\":\"ns\"") != base::StringView::npos) {
+    json_tracker->SetTimeUnit(json::TimeUnit::kNs);
+  } else if (view.find("\"displayTimeUnit\":\"ms\"") !=
+             base::StringView::npos) {
+    json_tracker->SetTimeUnit(json::TimeUnit::kMs);
+  }
+
   if (offset_ == 0) {
+    // Strip leading whitespace.
+    while (next != end && isspace(*next)) {
+      next++;
+    }
+    if (next == end) {
+      return util::ErrStatus(
+          "Failed to parse: first chunk has only whitespace");
+    }
+
     // Trace could begin in any of these ways:
     // {"traceEvents":[{
     // { "traceEvents": [{
     // [{
-    // Skip up to the first '['
-    while (next != end && *next != '[') {
-      next++;
+    if (*next != '{' && *next != '[') {
+      return util::ErrStatus(
+          "Failed to parse: first non-whitespace character is not [ or {");
     }
-    if (next == end)
-      return util::ErrStatus("Failed to parse: first chunk missing opening [");
+
+    // Figure out the format of the JSON file based on the first non-whitespace
+    // character.
+    format_ = *next == '{' ? TraceFormat::kOuterDictionary
+                           : TraceFormat::kOnlyTraceEvents;
+
+    // Skip the '[' or '{' character.
     next++;
+
+    // Set our current position based on the format of the trace.
+    position_ = format_ == TraceFormat::kOuterDictionary
+                    ? TracePosition::kDictionaryKey
+                    : TracePosition::kTraceEventsArray;
   }
 
-  auto* trace_sorter = context_->sorter.get();
-
-  while (next < end) {
-    std::unique_ptr<Json::Value> value(new Json::Value());
-    const auto res = ReadOneJsonDict(next, end, value.get(), &next);
-    if (res == kFatalError)
-      return util::ErrStatus("Encountered fatal error while parsing JSON");
-    if (res == kEndOfTrace || res == kNeedsMoreData)
-      break;
-
-    base::Optional<int64_t> opt_ts =
-        json_trace_utils::CoerceToNs((*value)["ts"]);
-    if (!opt_ts.has_value()) {
-      context_->storage->IncrementStats(stats::json_tokenizer_failure);
-      continue;
-    }
-    int64_t ts = opt_ts.value();
-
-    trace_sorter->PushJsonValue(ts, std::move(value));
-  }
+  auto status = ParseInternal(next, end, &next);
+  if (!status.ok())
+    return status;
 
   offset_ += static_cast<uint64_t>(next - buf);
   buffer_.erase(buffer_.begin(), buffer_.begin() + (next - buf));
   return util::OkStatus();
+#else
+  perfetto::base::ignore_result(data);
+  perfetto::base::ignore_result(size);
+  perfetto::base::ignore_result(context_);
+  perfetto::base::ignore_result(format_);
+  perfetto::base::ignore_result(position_);
+  perfetto::base::ignore_result(offset_);
+  return util::ErrStatus("Cannot parse JSON trace due to missing JSON support");
+#endif
 }
 
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
+util::Status JsonTraceTokenizer::ParseInternal(const char* start,
+                                               const char* end,
+                                               const char** out) {
+  PERFETTO_DCHECK(json::IsJsonSupported());
+  JsonTracker* json_tracker = JsonTracker::GetOrCreate(context_);
+  auto* trace_sorter = context_->sorter.get();
+
+  const char* next = start;
+  switch (position_) {
+    case TracePosition::kDictionaryKey: {
+      if (format_ != TraceFormat::kOuterDictionary) {
+        return util::ErrStatus(
+            "Failed to parse: illegal JSON format when parsing dictionary key");
+      }
+
+      std::string key;
+      auto res = ReadOneJsonKey(start, end, &key, &next);
+      if (res == ReadKeyRes::kFatalError)
+        return util::ErrStatus("Encountered fatal error while parsing JSON");
+
+      if (res == ReadKeyRes::kEndOfDictionary ||
+          res == ReadKeyRes::kNeedsMoreData) {
+        break;
+      }
+
+      if (key == "traceEvents") {
+        position_ = TracePosition::kTraceEventsArray;
+        return ParseInternal(next + 1, end, out);
+      } else if (key == "systemTraceEvents") {
+        position_ = TracePosition::kSystemTraceEventsString;
+        return ParseInternal(next + 1, end, out);
+      } else if (key == "metadata") {
+        position_ = TracePosition::kWaitingForMetadataDictionary;
+        return ParseInternal(next + 1, end, out);
+      } else {
+        // If we don't recognize the key, just ignore the rest of the trace and
+        // go to EOF.
+        // TODO(lalitm): do something better here.
+        position_ = TracePosition::kEof;
+        break;
+      }
+    }
+    case TracePosition::kSystemTraceEventsString: {
+      if (format_ != TraceFormat::kOuterDictionary) {
+        return util::ErrStatus(
+            "Failed to parse: illegal JSON format when parsing system events");
+      }
+
+      while (next < end) {
+        std::string raw_line;
+        auto res = ReadOneSystemTraceLine(next, end, &raw_line, &next);
+        if (res == ReadSystemLineRes::kFatalError)
+          return util::ErrStatus("Encountered fatal error while parsing JSON");
+
+        if (res == ReadSystemLineRes::kNeedsMoreData)
+          break;
+
+        if (res == ReadSystemLineRes::kEndOfSystemTrace) {
+          position_ = TracePosition::kDictionaryKey;
+          return ParseInternal(next, end, out);
+        }
+
+        if (base::StartsWith(raw_line, "#") || raw_line.empty())
+          continue;
+
+        std::unique_ptr<SystraceLine> line(new SystraceLine());
+        util::Status status =
+            systrace_line_tokenizer_.Tokenize(raw_line, line.get());
+        if (!status.ok())
+          return status;
+        trace_sorter->PushSystraceLine(std::move(line));
+      }
+      break;
+    }
+    case TracePosition::kWaitingForMetadataDictionary: {
+      if (format_ != TraceFormat::kOuterDictionary) {
+        return util::ErrStatus(
+            "Failed to parse: illegal JSON format when parsing metadata");
+      }
+
+      std::unique_ptr<Json::Value> value(new Json::Value());
+      const auto res = ReadOneJsonDict(next, end, value.get(), &next);
+      if (res == ReadDictRes::kFatalError || res == ReadDictRes::kEndOfArray)
+        return util::ErrStatus("Encountered fatal error while parsing JSON");
+      if (res == ReadDictRes::kEndOfTrace ||
+          res == ReadDictRes::kNeedsMoreData) {
+        break;
+      }
+
+      // TODO(lalitm): read and ingest the relevant data inside |value|.
+      position_ = TracePosition::kDictionaryKey;
+      break;
+    }
+    case TracePosition::kTraceEventsArray: {
+      while (next < end) {
+        std::unique_ptr<Json::Value> value(new Json::Value());
+        const auto res = ReadOneJsonDict(next, end, value.get(), &next);
+        if (res == ReadDictRes::kFatalError)
+          return util::ErrStatus("Encountered fatal error while parsing JSON");
+        if (res == ReadDictRes::kEndOfTrace ||
+            res == ReadDictRes::kNeedsMoreData) {
+          break;
+        }
+
+        if (res == ReadDictRes::kEndOfArray) {
+          position_ = format_ == TraceFormat::kOuterDictionary
+                          ? TracePosition::kDictionaryKey
+                          : TracePosition::kEof;
+          break;
+        }
+
+        base::Optional<int64_t> opt_ts =
+            json_tracker->CoerceToTs((*value)["ts"]);
+        int64_t ts = 0;
+        if (opt_ts.has_value()) {
+          ts = opt_ts.value();
+        } else {
+          // Metadata events may omit ts. In all other cases error:
+          auto& ph = (*value)["ph"];
+          if (!ph.isString() || *ph.asCString() != 'M') {
+            context_->storage->IncrementStats(stats::json_tokenizer_failure);
+            continue;
+          }
+        }
+        trace_sorter->PushJsonValue(ts, std::move(value));
+      }
+      break;
+    }
+    case TracePosition::kEof: {
+      break;
+    }
+  }
+  *out = next;
+  return util::OkStatus();
+}
+#endif
+
+void JsonTraceTokenizer::NotifyEndOfFile() {}
+
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_TP_JSON_IMPORT)
diff --git a/src/trace_processor/importers/json/json_trace_tokenizer.h b/src/trace_processor/importers/json/json_trace_tokenizer.h
index 9eb15b3..d69189f 100644
--- a/src/trace_processor/importers/json/json_trace_tokenizer.h
+++ b/src/trace_processor/importers/json/json_trace_tokenizer.h
@@ -20,7 +20,8 @@
 #include <stdint.h>
 
 #include "src/trace_processor/chunked_trace_reader.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/importers/systrace/systrace_line_tokenizer.h"
+#include "src/trace_processor/storage/trace_storage.h"
 
 namespace Json {
 class Value;
@@ -31,15 +32,60 @@
 
 class TraceProcessorContext;
 
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
 // Visible for testing.
-enum ReadDictRes { kFoundDict, kNeedsMoreData, kEndOfTrace, kFatalError };
+enum class ReadDictRes {
+  kFoundDict,
+  kNeedsMoreData,
+  kEndOfTrace,
+  kEndOfArray,
+  kFatalError,
+};
 
+// Parses at most one JSON dictionary and returns a pointer to the end of it,
+// or nullptr if no dict could be detected.
+// This is to avoid decoding the full trace in memory and reduce heap traffic.
+// E.g.  input:  { a:1 b:{ c:2, d:{ e:3 } } } , { a:4, ... },
+//       output: [   only this is parsed    ] ^return value points here.
 // Visible for testing.
 ReadDictRes ReadOneJsonDict(const char* start,
                             const char* end,
                             Json::Value* value,
                             const char** next);
 
+enum class ReadKeyRes {
+  kFoundKey,
+  kNeedsMoreData,
+  kEndOfDictionary,
+  kFatalError,
+};
+
+// Parses at most one JSON key and returns a pointer to the start of the value
+// associated with that key.
+// This is to avoid decoding the full trace in memory and reduce heap traffic.
+// E.g. input:  a:1 b:{ c:2}}
+//     output:    ^ return value points here, key is set to "a".
+// Note: even if the whole key may be available, this method will return
+// kNeedsMoreData until the first character of the value is available.
+// Visible for testing.
+ReadKeyRes ReadOneJsonKey(const char* start,
+                          const char* end,
+                          std::string* key,
+                          const char** next);
+
+enum class ReadSystemLineRes {
+  kFoundLine,
+  kNeedsMoreData,
+  kEndOfSystemTrace,
+  kFatalError,
+};
+
+ReadSystemLineRes ReadOneSystemTraceLine(const char* start,
+                                         const char* end,
+                                         std::string* line,
+                                         const char** next);
+#endif
+
 // Reads a JSON trace in chunks and extracts top level json objects.
 class JsonTraceTokenizer : public ChunkedTraceReader {
  public:
@@ -48,10 +94,55 @@
 
   // ChunkedTraceReader implementation.
   util::Status Parse(std::unique_ptr<uint8_t[]>, size_t) override;
+  void NotifyEndOfFile() override;
 
  private:
+  // Enum which tracks which type of JSON trace we are dealing with.
+  enum class TraceFormat {
+    // Enum value when ther outer-most layer is a dictionary with multiple
+    // key value pairs
+    kOuterDictionary,
+
+    // Enum value when we only have trace events (i.e. the outermost
+    // layer is just a array of trace events).
+    kOnlyTraceEvents,
+  };
+
+  // Enum which tracks our current position within the trace.
+  enum class TracePosition {
+    // This indicates that we are inside the outermost dictionary of the
+    // trace and need to read the next key of the dictionary.
+    // This position is only valid when the |format_| == |kOuterDictionary|.
+    kDictionaryKey,
+
+    // This indicates we are inside the systemTraceEvents string.
+    // This position is only valid when the |format_| == |kOuterDictionary|.
+    kSystemTraceEventsString,
+
+    // This indicates we are waiting for the entire metadata dictionary to be
+    // available.
+    kWaitingForMetadataDictionary,
+
+    // This indicates where are inside the traceEvents array.
+    kTraceEventsArray,
+
+    // This indicates we cannot parse any more data in the trace.
+    kEof,
+  };
+
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
+  util::Status ParseInternal(const char* start,
+                             const char* end,
+                             const char** next);
+#endif
+
   TraceProcessorContext* const context_;
 
+  TraceFormat format_ = TraceFormat::kOuterDictionary;
+  TracePosition position_ = TracePosition::kDictionaryKey;
+
+  SystraceLineTokenizer systrace_line_tokenizer_;
+
   uint64_t offset_ = 0;
   // Used to glue together JSON objects that span across two (or more)
   // Parse boundaries.
diff --git a/src/trace_processor/importers/json/json_trace_tokenizer_unittest.cc b/src/trace_processor/importers/json/json_trace_tokenizer_unittest.cc
index 4305120..4bae1a7 100644
--- a/src/trace_processor/importers/json/json_trace_tokenizer_unittest.cc
+++ b/src/trace_processor/importers/json/json_trace_tokenizer_unittest.cc
@@ -24,66 +24,209 @@
 namespace trace_processor {
 namespace {
 
-TEST(JsonTraceTokenizerTest, Success) {
+TEST(JsonTraceTokenizerTest, ReadDictSuccess) {
   const char* start = R"({ "foo": "bar" })";
   const char* end = start + strlen(start);
   const char* next = nullptr;
   Json::Value value;
   ReadDictRes result = ReadOneJsonDict(start, end, &value, &next);
 
-  ASSERT_EQ(result, kFoundDict);
+  ASSERT_EQ(result, ReadDictRes::kFoundDict);
   ASSERT_EQ(next, end);
   ASSERT_EQ(value["foo"].asString(), "bar");
 }
 
-TEST(JsonTraceTokenizerTest, QuotedBraces) {
+TEST(JsonTraceTokenizerTest, ReadDictQuotedBraces) {
   const char* start = R"({ "foo": "}\"bar{\\" })";
   const char* end = start + strlen(start);
   const char* next = nullptr;
   Json::Value value;
   ReadDictRes result = ReadOneJsonDict(start, end, &value, &next);
 
-  ASSERT_EQ(result, kFoundDict);
+  ASSERT_EQ(result, ReadDictRes::kFoundDict);
   ASSERT_EQ(next, end);
   ASSERT_EQ(value["foo"].asString(), "}\"bar{\\");
 }
 
-TEST(JsonTraceTokenizerTest, TwoDicts) {
+TEST(JsonTraceTokenizerTest, ReadDictTwoDicts) {
   const char* start = R"({"foo": 1}, {"bar": 2})";
   const char* middle = start + strlen(R"({"foo": 1})");
   const char* end = start + strlen(start);
   const char* next = nullptr;
   Json::Value value;
 
-  ASSERT_EQ(ReadOneJsonDict(start, end, &value, &next), kFoundDict);
+  ASSERT_EQ(ReadOneJsonDict(start, end, &value, &next),
+            ReadDictRes::kFoundDict);
   ASSERT_EQ(next, middle);
   ASSERT_EQ(value["foo"].asInt(), 1);
 
-  ASSERT_EQ(ReadOneJsonDict(next, end, &value, &next), kFoundDict);
+  ASSERT_EQ(ReadOneJsonDict(next, end, &value, &next), ReadDictRes::kFoundDict);
   ASSERT_EQ(next, end);
   ASSERT_EQ(value["bar"].asInt(), 2);
 }
 
-TEST(JsonTraceTokenizerTest, NeedMoreData) {
+TEST(JsonTraceTokenizerTest, ReadDictNeedMoreData) {
   const char* start = R"({"foo": 1)";
   const char* end = start + strlen(start);
   const char* next = nullptr;
   Json::Value value;
 
-  ASSERT_EQ(ReadOneJsonDict(start, end, &value, &next), kNeedsMoreData);
+  ASSERT_EQ(ReadOneJsonDict(start, end, &value, &next),
+            ReadDictRes::kNeedsMoreData);
   ASSERT_EQ(next, nullptr);
 }
 
-TEST(JsonTraceTokenizerTest, FatalError) {
+TEST(JsonTraceTokenizerTest, ReadDictFatalError) {
   const char* start = R"({helloworld})";
   const char* end = start + strlen(start);
   const char* next = nullptr;
   Json::Value value;
 
-  ASSERT_EQ(ReadOneJsonDict(start, end, &value, &next), kFatalError);
+  ASSERT_EQ(ReadOneJsonDict(start, end, &value, &next),
+            ReadDictRes::kFatalError);
   ASSERT_EQ(next, nullptr);
 }
 
+TEST(JsonTraceTokenizerTest, ReadKeyIntValue) {
+  const char* start = R"("Test": 01234, )";
+  const char* middle = start + strlen(R"("Test": )");
+  const char* end = start + strlen(start);
+  const char* next = nullptr;
+  std::string key;
+
+  ASSERT_EQ(ReadOneJsonKey(start, end, &key, &next), ReadKeyRes::kFoundKey);
+  ASSERT_EQ(next, middle);
+  ASSERT_EQ(key, "Test");
+}
+
+TEST(JsonTraceTokenizerTest, ReadKeyArrayValue) {
+  const char* start = R"(, "key": [test], )";
+  const char* middle = start + strlen(R"(, "key": )");
+  const char* end = start + strlen(start);
+  const char* next = nullptr;
+  std::string key;
+
+  ASSERT_EQ(ReadOneJsonKey(start, end, &key, &next), ReadKeyRes::kFoundKey);
+  ASSERT_EQ(next, middle);
+  ASSERT_EQ(key, "key");
+}
+
+TEST(JsonTraceTokenizerTest, ReadKeyDictValue) {
+  const char* start = R"("key2": {}})";
+  const char* middle = start + strlen(R"("key2": )");
+  const char* end = start + strlen(start);
+  const char* next = nullptr;
+  std::string key;
+
+  ASSERT_EQ(ReadOneJsonKey(start, end, &key, &next), ReadKeyRes::kFoundKey);
+  ASSERT_EQ(next, middle);
+  ASSERT_EQ(key, "key2");
+}
+
+TEST(JsonTraceTokenizerTest, ReadKeyEscaped) {
+  const char* start = R"("key\n2": {}})";
+  const char* middle = start + strlen(R"("key\n2": )");
+  const char* end = start + strlen(start);
+  const char* next = nullptr;
+  std::string key;
+
+  ASSERT_EQ(ReadOneJsonKey(start, end, &key, &next), ReadKeyRes::kFoundKey);
+  ASSERT_EQ(next, middle);
+  ASSERT_EQ(key, "key\n2");
+}
+
+TEST(JsonTraceTokenizerTest, ReadKeyNeedMoreDataStartString) {
+  const char* start = R"(")";
+  const char* end = start + strlen(start);
+  const char* next = nullptr;
+  std::string key;
+
+  ASSERT_EQ(ReadOneJsonKey(start, end, &key, &next),
+            ReadKeyRes::kNeedsMoreData);
+  ASSERT_EQ(next, nullptr);
+}
+
+TEST(JsonTraceTokenizerTest, ReadKeyNeedMoreDataMiddleString) {
+  const char* start = R"("key)";
+  const char* end = start + strlen(start);
+  const char* next = nullptr;
+  std::string key;
+
+  ASSERT_EQ(ReadOneJsonKey(start, end, &key, &next),
+            ReadKeyRes::kNeedsMoreData);
+  ASSERT_EQ(next, nullptr);
+}
+
+TEST(JsonTraceTokenizerTest, ReadKeyNeedMoreDataNoValue) {
+  const char* start = R"("key": )";
+  const char* end = start + strlen(start);
+  const char* next = nullptr;
+  std::string key;
+
+  ASSERT_EQ(ReadOneJsonKey(start, end, &key, &next),
+            ReadKeyRes::kNeedsMoreData);
+  ASSERT_EQ(next, nullptr);
+}
+
+TEST(JsonTraceTokenizerTest, ReadKeyEndOfDict) {
+  const char* start = R"(      })";
+  const char* end = start + strlen(start);
+  const char* next = nullptr;
+  std::string key;
+
+  ASSERT_EQ(ReadOneJsonKey(start, end, &key, &next),
+            ReadKeyRes::kEndOfDictionary);
+  ASSERT_EQ(next, end);
+}
+
+TEST(JsonTraceTokenizerTest, ReadSystraceLine) {
+  const char* start = R"(test one two\n   test again\n)";
+  const char* middle = start + strlen(R"(test one two\n)");
+  const char* end = start + strlen(start);
+  const char* next = nullptr;
+  std::string line;
+
+  ASSERT_EQ(ReadOneSystemTraceLine(start, end, &line, &next),
+            ReadSystemLineRes::kFoundLine);
+  ASSERT_EQ(next, middle);
+  ASSERT_EQ(line, "test one two");
+}
+
+TEST(JsonTraceTokenizerTest, ReadSystraceLineEscaped) {
+  const char* start = R"(test\t one two\n   test again\n)";
+  const char* middle = start + strlen(R"(test\t one two\n)");
+  const char* end = start + strlen(start);
+  const char* next = nullptr;
+  std::string line;
+
+  ASSERT_EQ(ReadOneSystemTraceLine(start, end, &line, &next),
+            ReadSystemLineRes::kFoundLine);
+  ASSERT_EQ(next, middle);
+  ASSERT_EQ(line, "test\t one two");
+}
+
+TEST(JsonTraceTokenizerTest, ReadSystraceNeedMoreDataOnlyEscape) {
+  const char* start = R"(test one two\)";
+  const char* end = start + strlen(start);
+  const char* next = nullptr;
+  std::string line;
+
+  ASSERT_EQ(ReadOneSystemTraceLine(start, end, &line, &next),
+            ReadSystemLineRes::kNeedsMoreData);
+  ASSERT_EQ(next, nullptr);
+}
+
+TEST(JsonTraceTokenizerTest, ReadSystraceEndOfData) {
+  const char* start = R"(")";
+  const char* end = start + strlen(start);
+  const char* next = nullptr;
+  std::string line;
+
+  ASSERT_EQ(ReadOneSystemTraceLine(start, end, &line, &next),
+            ReadSystemLineRes::kEndOfSystemTrace);
+  ASSERT_EQ(next, end);
+}
+
 }  // namespace
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/json/json_trace_utils.cc b/src/trace_processor/importers/json/json_trace_utils.cc
deleted file mode 100644
index a4802ff..0000000
--- a/src/trace_processor/importers/json/json_trace_utils.cc
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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.
- */
-
-// For bazel build.
-#include "perfetto/base/build_config.h"
-#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON_IMPORT)
-
-#include "src/trace_processor/importers/json/json_trace_utils.h"
-
-#include <json/value.h>
-#include <limits>
-
-namespace perfetto {
-namespace trace_processor {
-namespace json_trace_utils {
-
-// Json trace event timestamps are in us.
-// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit#heading=h.nso4gcezn7n1
-base::Optional<int64_t> CoerceToNs(const Json::Value& value) {
-  switch (static_cast<size_t>(value.type())) {
-    case Json::realValue:
-      return static_cast<int64_t>(value.asDouble() * 1000);
-    case Json::uintValue:
-    case Json::intValue:
-      return value.asInt64() * 1000;
-    case Json::stringValue: {
-      std::string s = value.asString();
-      char* end;
-      int64_t n = strtoll(s.c_str(), &end, 10);
-      if (end != s.data() + s.size())
-        return base::nullopt;
-      return n * 1000;
-    }
-    default:
-      return base::nullopt;
-  }
-}
-
-base::Optional<int64_t> CoerceToInt64(const Json::Value& value) {
-  switch (static_cast<size_t>(value.type())) {
-    case Json::realValue:
-    case Json::uintValue:
-    case Json::intValue:
-      return value.asInt64();
-    case Json::stringValue: {
-      std::string s = value.asString();
-      char* end;
-      int64_t n = strtoll(s.c_str(), &end, 10);
-      if (end != s.data() + s.size())
-        return base::nullopt;
-      return n;
-    }
-    default:
-      return base::nullopt;
-  }
-}
-
-base::Optional<uint32_t> CoerceToUint32(const Json::Value& value) {
-  base::Optional<int64_t> result = CoerceToInt64(value);
-  if (!result.has_value())
-    return base::nullopt;
-  int64_t n = result.value();
-  if (n < 0 || n > std::numeric_limits<uint32_t>::max())
-    return base::nullopt;
-  return static_cast<uint32_t>(n);
-}
-
-}  // namespace json_trace_utils
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_TP_JSON_IMPORT)
diff --git a/src/trace_processor/importers/json/json_trace_utils.h b/src/trace_processor/importers/json/json_trace_utils.h
deleted file mode 100644
index 3a18641..0000000
--- a/src/trace_processor/importers/json/json_trace_utils.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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_IMPORTERS_JSON_JSON_TRACE_UTILS_H_
-#define SRC_TRACE_PROCESSOR_IMPORTERS_JSON_JSON_TRACE_UTILS_H_
-
-#include <stdint.h>
-
-#include "perfetto/ext/base/optional.h"
-
-namespace Json {
-class Value;
-}
-
-namespace perfetto {
-namespace trace_processor {
-
-class TraceProcessorContext;
-
-namespace json_trace_utils {
-
-base::Optional<int64_t> CoerceToNs(const Json::Value& value);
-base::Optional<int64_t> CoerceToInt64(const Json::Value& value);
-base::Optional<uint32_t> CoerceToUint32(const Json::Value& value);
-
-}  // namespace json_trace_utils
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_JSON_JSON_TRACE_UTILS_H_
diff --git a/src/trace_processor/destructible.cc b/src/trace_processor/importers/json/json_tracker.cc
similarity index 76%
copy from src/trace_processor/destructible.cc
copy to src/trace_processor/importers/json/json_tracker.cc
index 22bcf6a..971042a 100644
--- a/src/trace_processor/destructible.cc
+++ b/src/trace_processor/importers/json/json_tracker.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.
@@ -13,12 +13,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include "src/trace_processor/destructible.h"
+
+#include "src/trace_processor/importers/json/json_tracker.h"
 
 namespace perfetto {
 namespace trace_processor {
 
-Destructible::~Destructible() = default;
+JsonTracker::JsonTracker(TraceProcessorContext*) {}
+JsonTracker::~JsonTracker() = default;
 
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/json/json_tracker.h b/src/trace_processor/importers/json/json_tracker.h
new file mode 100644
index 0000000..ac24083
--- /dev/null
+++ b/src/trace_processor/importers/json/json_tracker.h
@@ -0,0 +1,58 @@
+/*
+ * 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_IMPORTERS_JSON_JSON_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_JSON_JSON_TRACKER_H_
+
+#include "src/trace_processor/importers/json/json_utils.h"
+#include "src/trace_processor/types/destructible.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace Json {
+class Value;
+}
+
+namespace perfetto {
+namespace trace_processor {
+
+class JsonTracker : public Destructible {
+ public:
+  JsonTracker(const JsonTracker&) = delete;
+  JsonTracker& operator=(const JsonTracker&) = delete;
+  explicit JsonTracker(TraceProcessorContext*);
+  virtual ~JsonTracker();
+
+  static JsonTracker* GetOrCreate(TraceProcessorContext* context) {
+    if (!context->json_tracker) {
+      context->json_tracker.reset(new JsonTracker(context));
+    }
+    return static_cast<JsonTracker*>(context->json_tracker.get());
+  }
+
+  void SetTimeUnit(json::TimeUnit time_unit) { time_unit_ = time_unit; }
+
+  base::Optional<int64_t> CoerceToTs(const Json::Value& value) {
+    return json::CoerceToTs(time_unit_, value);
+  }
+
+ private:
+  json::TimeUnit time_unit_ = json::TimeUnit::kUs;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_JSON_JSON_TRACKER_H_
diff --git a/src/trace_processor/importers/json/json_tracker_unittest.cc b/src/trace_processor/importers/json/json_tracker_unittest.cc
new file mode 100644
index 0000000..4ae6009
--- /dev/null
+++ b/src/trace_processor/importers/json/json_tracker_unittest.cc
@@ -0,0 +1,40 @@
+/*
+ * 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/json/json_tracker.h"
+
+#include "test/gtest_and_gmock.h"
+
+#include <json/value.h>
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+TEST(JsonTrackerTest, Ns) {
+  JsonTracker tracker(nullptr);
+  tracker.SetTimeUnit(json::TimeUnit::kNs);
+  ASSERT_EQ(tracker.CoerceToTs(Json::Value(42)).value_or(-1), 42);
+}
+
+TEST(JsonTraceUtilsTest, Us) {
+  JsonTracker tracker(nullptr);
+  ASSERT_EQ(tracker.CoerceToTs(Json::Value(42)).value_or(-1), 42000);
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/json/json_utils.cc b/src/trace_processor/importers/json/json_utils.cc
new file mode 100644
index 0000000..b9b1e65
--- /dev/null
+++ b/src/trace_processor/importers/json/json_utils.cc
@@ -0,0 +1,219 @@
+/*
+ * 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/json/json_utils.h"
+
+#include "perfetto/base/build_config.h"
+
+#include <limits>
+
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
+#include <json/reader.h>
+#endif
+
+namespace perfetto {
+namespace trace_processor {
+namespace json {
+namespace {
+
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
+int64_t TimeUnitToNs(TimeUnit unit) {
+  return static_cast<int64_t>(unit);
+}
+#endif
+
+}  // namespace
+
+bool IsJsonSupported() {
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
+  return true;
+#else
+  return false;
+#endif
+}
+
+base::Optional<int64_t> CoerceToTs(TimeUnit unit, const Json::Value& value) {
+  PERFETTO_DCHECK(IsJsonSupported());
+
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
+  switch (static_cast<size_t>(value.type())) {
+    case Json::realValue:
+      return static_cast<int64_t>(value.asDouble() * TimeUnitToNs(unit));
+    case Json::uintValue:
+    case Json::intValue:
+      return value.asInt64() * TimeUnitToNs(unit);
+    case Json::stringValue: {
+      std::string s = value.asString();
+      char* end;
+      int64_t n = strtoll(s.c_str(), &end, 10);
+      if (end != s.data() + s.size())
+        return base::nullopt;
+      return n * TimeUnitToNs(unit);
+    }
+    default:
+      return base::nullopt;
+  }
+#else
+  perfetto::base::ignore_result(unit);
+  perfetto::base::ignore_result(value);
+  return base::nullopt;
+#endif
+}
+
+base::Optional<int64_t> CoerceToInt64(const Json::Value& value) {
+  PERFETTO_DCHECK(IsJsonSupported());
+
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
+  switch (static_cast<size_t>(value.type())) {
+    case Json::realValue:
+    case Json::uintValue:
+      return static_cast<int64_t>(value.asUInt64());
+    case Json::intValue:
+      return value.asInt64();
+    case Json::stringValue: {
+      std::string s = value.asString();
+      char* end;
+      int64_t n = strtoll(s.c_str(), &end, 10);
+      if (end != s.data() + s.size())
+        return base::nullopt;
+      return n;
+    }
+    default:
+      return base::nullopt;
+  }
+#else
+  perfetto::base::ignore_result(value);
+  return base::nullopt;
+#endif
+}
+
+base::Optional<uint32_t> CoerceToUint32(const Json::Value& value) {
+  PERFETTO_DCHECK(IsJsonSupported());
+
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
+  base::Optional<int64_t> result = CoerceToInt64(value);
+  if (!result.has_value())
+    return base::nullopt;
+  int64_t n = result.value();
+  if (n < 0 || n > std::numeric_limits<uint32_t>::max())
+    return base::nullopt;
+  return static_cast<uint32_t>(n);
+#else
+  perfetto::base::ignore_result(value);
+  return base::nullopt;
+#endif
+}
+
+base::Optional<Json::Value> ParseJsonString(base::StringView raw_string) {
+  PERFETTO_DCHECK(IsJsonSupported());
+
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
+  Json::Reader reader;
+  Json::Value value;
+  const char* begin = raw_string.data();
+  return reader.parse(begin, begin + raw_string.size(), value)
+             ? base::make_optional(std::move(value))
+             : base::nullopt;
+#else
+  perfetto::base::ignore_result(raw_string);
+  return base::nullopt;
+#endif
+}
+
+bool AddJsonValueToArgs(const Json::Value& value,
+                        base::StringView flat_key,
+                        base::StringView key,
+                        TraceStorage* storage,
+                        ArgsTracker::BoundInserter* inserter) {
+  PERFETTO_DCHECK(IsJsonSupported());
+
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
+  if (value.isObject()) {
+    auto it = value.begin();
+    bool inserted = false;
+    for (; it != value.end(); ++it) {
+      std::string child_name = it.memberName();
+      std::string child_flat_key = flat_key.ToStdString() + "." + child_name;
+      std::string child_key = key.ToStdString() + "." + child_name;
+      inserted |=
+          AddJsonValueToArgs(*it, base::StringView(child_flat_key),
+                             base::StringView(child_key), storage, inserter);
+    }
+    return inserted;
+  }
+
+  if (value.isArray()) {
+    auto it = value.begin();
+    bool inserted_any = false;
+    std::string array_key = key.ToStdString();
+    StringId array_key_id = storage->InternString(key);
+    for (; it != value.end(); ++it) {
+      size_t array_index = inserter->GetNextArrayEntryIndex(array_key_id);
+      std::string child_key =
+          array_key + "[" + std::to_string(array_index) + "]";
+      bool inserted = AddJsonValueToArgs(
+          *it, flat_key, base::StringView(child_key), storage, inserter);
+      if (inserted)
+        inserter->IncrementArrayEntryIndex(array_key_id);
+      inserted_any |= inserted;
+    }
+    return inserted_any;
+  }
+
+  // Leaf value.
+  auto flat_key_id = storage->InternString(flat_key);
+  auto key_id = storage->InternString(key);
+
+  switch (value.type()) {
+    case Json::ValueType::nullValue:
+      break;
+    case Json::ValueType::intValue:
+      inserter->AddArg(flat_key_id, key_id, Variadic::Integer(value.asInt64()));
+      return true;
+    case Json::ValueType::uintValue:
+      inserter->AddArg(flat_key_id, key_id,
+                       Variadic::UnsignedInteger(value.asUInt64()));
+      return true;
+    case Json::ValueType::realValue:
+      inserter->AddArg(flat_key_id, key_id, Variadic::Real(value.asDouble()));
+      return true;
+    case Json::ValueType::stringValue:
+      inserter->AddArg(flat_key_id, key_id,
+                       Variadic::String(storage->InternString(
+                           base::StringView(value.asString()))));
+      return true;
+    case Json::ValueType::booleanValue:
+      inserter->AddArg(flat_key_id, key_id, Variadic::Boolean(value.asBool()));
+      return true;
+    case Json::ValueType::objectValue:
+    case Json::ValueType::arrayValue:
+      PERFETTO_FATAL("Non-leaf types handled above");
+      break;
+  }
+  return false;
+#else
+  perfetto::base::ignore_result(value);
+  perfetto::base::ignore_result(flat_key);
+  perfetto::base::ignore_result(key);
+  perfetto::base::ignore_result(storage);
+  perfetto::base::ignore_result(inserter);
+  return false;
+#endif
+}
+
+}  // namespace json
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/json/json_utils.h b/src/trace_processor/importers/json/json_utils.h
new file mode 100644
index 0000000..f4a70b3
--- /dev/null
+++ b/src/trace_processor/importers/json/json_utils.h
@@ -0,0 +1,68 @@
+/*
+ * 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_IMPORTERS_JSON_JSON_UTILS_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_JSON_JSON_UTILS_H_
+
+#include <stdint.h>
+
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/string_view.h"
+
+#include "src/trace_processor/importers/common/args_tracker.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
+#include <json/value.h>
+#else
+namespace Json {
+class Value {};
+}  // namespace Json
+#endif
+
+namespace perfetto {
+namespace trace_processor {
+namespace json {
+
+// Returns whether JSON related functioanlity is supported with the current
+// build flags.
+bool IsJsonSupported();
+
+enum class TimeUnit { kNs = 1, kUs = 1000, kMs = 1000000 };
+base::Optional<int64_t> CoerceToTs(TimeUnit unit, const Json::Value& value);
+base::Optional<int64_t> CoerceToInt64(const Json::Value& value);
+base::Optional<uint32_t> CoerceToUint32(const Json::Value& value);
+
+// Parses the given JSON string into a JSON::Value object.
+// This function should only be called if |IsJsonSupported()| returns true.
+base::Optional<Json::Value> ParseJsonString(base::StringView raw_string);
+
+// Flattens the given Json::Value and adds each leaf node to the bound args
+// inserter. Note:
+//  * |flat_key| and |key| should be non-empty and will be used to prefix the
+//    keys of all leaf nodes in the JSON.
+//  * |storage| is used to intern all strings (e.g. keys and values).
+//  * This function should only be called if |IsJsonSupported()| returns true.
+bool AddJsonValueToArgs(const Json::Value& value,
+                        base::StringView flat_key,
+                        base::StringView key,
+                        TraceStorage* storage,
+                        ArgsTracker::BoundInserter* inserter);
+
+}  // namespace json
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_JSON_JSON_UTILS_H_
diff --git a/src/trace_processor/importers/json/json_trace_utils_unittest.cc b/src/trace_processor/importers/json/json_utils_unittest.cc
similarity index 60%
rename from src/trace_processor/importers/json/json_trace_utils_unittest.cc
rename to src/trace_processor/importers/json/json_utils_unittest.cc
index fcc115d..4134cc6 100644
--- a/src/trace_processor/importers/json/json_trace_utils_unittest.cc
+++ b/src/trace_processor/importers/json/json_utils_unittest.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/importers/json/json_trace_utils.h"
+#include "src/trace_processor/importers/json/json_utils.h"
 
 #include <json/value.h>
 
@@ -22,7 +22,7 @@
 
 namespace perfetto {
 namespace trace_processor {
-namespace json_trace_utils {
+namespace json {
 namespace {
 
 TEST(JsonTraceUtilsTest, CoerceToUint32) {
@@ -37,17 +37,24 @@
   ASSERT_EQ(CoerceToInt64(Json::Value(42.1)).value_or(-1), 42);
   ASSERT_FALSE(CoerceToInt64(Json::Value("foo")).has_value());
   ASSERT_FALSE(CoerceToInt64(Json::Value("1234!")).has_value());
+
+  Json::UInt64 n = 18446744073709551615UL;
+  ASSERT_EQ(CoerceToInt64(Json::Value{n}).value_or(0), -1);
 }
 
-TEST(JsonTraceUtilsTest, CoerceToNs) {
-  ASSERT_EQ(CoerceToNs(Json::Value(42)).value_or(-1), 42000);
-  ASSERT_EQ(CoerceToNs(Json::Value("42")).value_or(-1), 42000);
-  ASSERT_EQ(CoerceToNs(Json::Value(42.1)).value_or(-1), 42100);
-  ASSERT_FALSE(CoerceToNs(Json::Value("foo")).has_value());
-  ASSERT_FALSE(CoerceToNs(Json::Value("1234!")).has_value());
+TEST(JsonTraceUtilsTest, CoerceToTs) {
+  ASSERT_EQ(CoerceToTs(TimeUnit::kUs, Json::Value(42)).value_or(-1), 42000);
+  ASSERT_EQ(CoerceToTs(TimeUnit::kUs, Json::Value("42")).value_or(-1), 42000);
+  ASSERT_EQ(CoerceToTs(TimeUnit::kUs, Json::Value(42.1)).value_or(-1), 42100);
+  ASSERT_EQ(CoerceToTs(TimeUnit::kNs, Json::Value(42)).value_or(-1), 42);
+  ASSERT_EQ(CoerceToTs(TimeUnit::kMs, Json::Value(42)).value_or(-1), 42000000);
+  ASSERT_FALSE(CoerceToTs(TimeUnit::kNs, Json::Value("foo")).has_value());
+  ASSERT_FALSE(CoerceToTs(TimeUnit::kNs, Json::Value("1234!")).has_value());
+  ASSERT_FALSE(CoerceToTs(TimeUnit::kUs, Json::Value("1234!")).has_value());
+  ASSERT_FALSE(CoerceToTs(TimeUnit::kMs, Json::Value("1234!")).has_value());
 }
 
 }  // namespace
-}  // namespace json_trace_utils
+}  // namespace json
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/ninja/ninja_log_parser.cc b/src/trace_processor/importers/ninja/ninja_log_parser.cc
new file mode 100644
index 0000000..1618bc7
--- /dev/null
+++ b/src/trace_processor/importers/ninja/ninja_log_parser.cc
@@ -0,0 +1,189 @@
+/*
+ * 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/ninja/ninja_log_parser.h"
+#include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/trace_sorter.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+using base::StringSplitter;
+
+NinjaLogParser::NinjaLogParser(TraceProcessorContext* ctx) : ctx_(ctx) {}
+NinjaLogParser::~NinjaLogParser() = default;
+
+util::Status NinjaLogParser::Parse(std::unique_ptr<uint8_t[]> buf, size_t len) {
+  // A trace is read in chunks of arbitrary size (for http fetch() pipeliniing),
+  // not necessarily aligned on a line boundary.
+  // Here we push everything into a vector and, on each call, consume only
+  // the leading part until the last \n, keeping the rest for the next call.
+  const char* src = reinterpret_cast<const char*>(&buf[0]);
+  log_.insert(log_.end(), src, src + len);
+
+  // Find the last \n.
+  size_t valid_size = log_.size();
+  for (; valid_size > 0 && log_[valid_size - 1] != '\n'; --valid_size) {
+  }
+
+  for (StringSplitter line(log_.data(), valid_size, '\n'); line.Next();) {
+    static const char kHeader[] = "# ninja log v";
+    if (!header_parsed_) {
+      if (!base::StartsWith(line.cur_token(), kHeader))
+        return util::ErrStatus("Failed to parse ninja log header");
+      header_parsed_ = true;
+      auto version = base::CStringToUInt32(line.cur_token() + strlen(kHeader));
+      if (!version || *version != 5)
+        return util::ErrStatus("Unsupported ninja log version");
+      continue;
+    }
+
+    // Each line in the ninja log looks like this:
+    // 4 12  1579224178  ui/assets/modal.scss  832a958a9e234dfa
+    // Where:
+    // - [4, 12] are the timestamps in ms of [start, end] of the job, measured
+    //     from the beginning of the build.
+    // - 1579224178 is the "restat" (ignored).
+    // - ui/assets/modal.scss is the name of the output file being built.
+    // - 832a958a9e234dfa is a hash of the compiler invocation.
+    // In most cases, each hash should be unique per ninja invocation (because
+    // two rules shouln't generate the same output). However, in rare
+    // circumstances the same hash can show up more than once. Examples:
+    // - A GN action generates > 1 output per invocation (e.g., protos). In this
+    //   case all items will have the same [start, end] timestamp. In this case
+    //   we want to merge all the output names into one build step, because from
+    //   the build system viewpoint, that was the same compiler/tool invocation.
+    // - A subtle script that generates different outputs without taking a
+    //   --output=filename argument (e.g. via env vars or similar). Note that
+    //   this happens in the perfetto codebase itself (goto.google.com/nigew).
+    //   In this case we want to treat the two entries as two distinct jobs.
+    //
+    // In summary the deduping logic here is: if both the hash and the
+    // timestamps match -> merge, if not, keep distinct.
+    StringSplitter tok(&line, '\t');
+    auto t_start = base::CStringToInt64(tok.Next() ? tok.cur_token() : "");
+    auto t_end = base::CStringToInt64(tok.Next() ? tok.cur_token() : "");
+    tok.Next();  // Ignore restat.
+    const char* name = tok.Next() ? tok.cur_token() : nullptr;
+    auto cmdhash = base::CStringToUInt64(tok.Next() ? tok.cur_token() : "", 16);
+
+    if (!t_start || !t_end || !name || !cmdhash) {
+      ctx_->storage->IncrementStats(stats::ninja_parse_errors);
+      continue;
+    }
+
+    // The same log file can contain timestamps for different builds. The only
+    // way we can tell when a new build starts is by detecting the end timestamp
+    // breaking monotonicity.
+    if (last_end_seen_ == 0 || *t_end < last_end_seen_) {
+      // Create a new "process" for each build. In the UI this causes each build
+      // to be nested under a track group. |cur_build_id_| is the fake pid
+      // of the synthesized process.
+      ++cur_build_id_;
+      StringId name_id = ctx_->storage->InternString("Build");
+      ctx_->process_tracker->SetProcessNameIfUnset(
+          ctx_->process_tracker->GetOrCreateProcess(cur_build_id_), name_id);
+    }
+    last_end_seen_ = *t_end;
+
+    // If more hashes show up back-to-back with the same timestamps, merge them
+    // together as they identify multiple outputs for the same build rule.
+    if (!jobs_.empty() && *cmdhash == jobs_.back().hash &&
+        *t_start == jobs_.back().start_ms && *t_end == jobs_.back().end_ms) {
+      jobs_.back().names.append(" ");
+      jobs_.back().names.append(name);
+      continue;
+    }
+
+    jobs_.emplace_back(cur_build_id_, *t_start, *t_end, *cmdhash, name);
+  }
+  log_.erase(log_.begin(), log_.begin() + static_cast<ssize_t>(valid_size));
+  return util::OkStatus();
+}
+
+// This is called after the last Parase() call. At this point all |jobs_| have
+// been populated.
+void NinjaLogParser::NotifyEndOfFile() {
+  std::sort(jobs_.begin(), jobs_.end(),
+            [](const Job& x, const Job& y) { return x.start_ms < y.start_ms; });
+
+  // Now we need to work out the job parallelism. There's no direct indication
+  // of that in the ninja logs, so it must be inferred by observing overlapping
+  // of timestamps. In this context a "Worker" is an inferred sequence of jobs
+  // that happened concurrently with other sequences.
+  // Here we pack jobs according the following heuristic, for the sake of making
+  // the graph nicer to read to humans. Consider the initial situation:
+  // 1: [  job 1 ]
+  // 2:   [   job 2   ]
+  // 3: [   job 3   ]
+  //    T=0              | T=6
+  // Assume that a new job starts at T=6. It's very likely that job4 was started
+  // as a consequence of job2 completion (othewise it could have been started
+  // earlier, soon after job 1 or Job 3). It seems to make more sense to draw
+  // it next in the 2nd worker, i.e. next to job 2.
+  struct Worker {
+    int64_t busy_until;
+    TrackId track_id;
+  };
+  std::map<uint32_t /*build_id*/, std::vector<Worker>> workers_by_build;
+
+  // Assign thread ids to worker without conflicting with builds' process ids
+  // (to avoid main-thread auto-mapping).s
+  uint32_t last_worker_id = cur_build_id_;
+
+  for (const auto& job : jobs_) {
+    Worker* worker = nullptr;
+    auto& workers = workers_by_build[job.build_id];
+    for (Worker& cur : workers) {
+      // Pick the worker which has the greatest end time (busy_until) <= the
+      // job's start time.
+      if (cur.busy_until <= job.start_ms) {
+        if (!worker || cur.busy_until > worker->busy_until)
+          worker = &cur;
+      }
+    }
+    if (worker) {
+      // Update the worker's end time with the newly assigned job.
+      worker->busy_until = job.end_ms;
+    } else {
+      // All workers are busy, allocate a new one.
+      uint32_t worker_id = ++last_worker_id;
+      char name[32];
+      snprintf(name, sizeof(name), "Worker %zu", workers.size() + 1);
+      StringId name_id = ctx_->storage->InternString(name);
+      auto utid = ctx_->process_tracker->UpdateThread(worker_id, job.build_id);
+      ctx_->process_tracker->SetThreadNameIfUnset(utid, name_id);
+      TrackId track_id = ctx_->track_tracker->InternThreadTrack(utid);
+      workers.emplace_back(Worker{/*busy_until=*/job.end_ms, track_id});
+      worker = &workers.back();
+    }
+
+    static constexpr int64_t kMsToNs = 1000 * 1000;
+    const int64_t start_ns = job.start_ms * kMsToNs;
+    const int64_t dur_ns = (job.end_ms - job.start_ms) * kMsToNs;
+    StringId name_id = ctx_->storage->InternString(base::StringView(job.names));
+    ctx_->slice_tracker->Scoped(start_ns, worker->track_id, StringId::Null(),
+                                name_id, dur_ns);
+  }
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/ninja/ninja_log_parser.h b/src/trace_processor/importers/ninja/ninja_log_parser.h
new file mode 100644
index 0000000..280fa5a
--- /dev/null
+++ b/src/trace_processor/importers/ninja/ninja_log_parser.h
@@ -0,0 +1,81 @@
+/*
+ * 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_IMPORTERS_NINJA_NINJA_LOG_PARSER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_NINJA_NINJA_LOG_PARSER_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+
+#include "src/trace_processor/chunked_trace_reader.h"
+#include "src/trace_processor/trace_parser.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+// This class parses Ninja's (the build system, ninja-build.org) build logs and
+// turns them into traces. A ninja log typically contains the logs of >1 ninja
+// invocation. We map those as follows:
+// - For each ninja invocation we create one process in the trace (from the UI
+//   perspective a process is a group of tracks).
+// - Within each invocation we work out the parallelism from the time stamp and
+//   create one thread for each concurent stream of jobs.
+// Caveat: this works only if ninja didn't recompact the logs. Once recompaction
+// happens (can be forced via ninja -t recompact) there is no way to identify
+// the boundaries of each build (recompaction deletes, for each hash, all but
+// the most recent timestamp and rewrites the log).
+class NinjaLogParser : public ChunkedTraceReader {
+ public:
+  explicit NinjaLogParser(TraceProcessorContext*);
+  ~NinjaLogParser() override;
+  NinjaLogParser(const NinjaLogParser&) = delete;
+  NinjaLogParser& operator=(const NinjaLogParser&) = delete;
+
+  // ChunkedTraceReader implementation
+  util::Status Parse(std::unique_ptr<uint8_t[]>, size_t) override;
+  void NotifyEndOfFile() override;
+
+ private:
+  struct Job {
+    Job(uint32_t b, int64_t s, int64_t e, uint64_t h, const std::string& n)
+        : build_id(b), start_ms(s), end_ms(e), hash(h), names(n) {}
+
+    uint32_t build_id;  // The synthesized PID of the build "process".
+    int64_t start_ms;
+    int64_t end_ms;
+    uint64_t hash;  // Hash of the compiler invocation cmdline.
+
+    // Typically the one output for the compiler invocation. In case of actions
+    // generating multiple outputs this contains the join of all output names.
+    std::string names;
+  };
+
+  TraceProcessorContext* const ctx_;
+  bool header_parsed_ = false;
+  int64_t last_end_seen_ = 0;
+  uint32_t cur_build_id_ = 0;
+  std::vector<Job> jobs_;
+  std::vector<char> log_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_NINJA_NINJA_LOG_PARSER_H_
diff --git a/src/trace_processor/importers/proto/android_probes_parser.cc b/src/trace_processor/importers/proto/android_probes_parser.cc
index 1952fef..fba0c10 100644
--- a/src/trace_processor/importers/proto/android_probes_parser.cc
+++ b/src/trace_processor/importers/proto/android_probes_parser.cc
@@ -18,13 +18,13 @@
 
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/traced/sys_stats_counters.h"
-#include "src/trace_processor/args_tracker.h"
-#include "src/trace_processor/clock_tracker.h"
-#include "src/trace_processor/event_tracker.h"
-#include "src/trace_processor/metadata_tracker.h"
-#include "src/trace_processor/process_tracker.h"
-#include "src/trace_processor/syscall_tracker.h"
-#include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/clock_tracker.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/proto/metadata_tracker.h"
+#include "src/trace_processor/importers/syscalls/syscall_tracker.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 #include "protos/perfetto/common/android_log_constants.pbzero.h"
 #include "protos/perfetto/config/trace_config.pbzero.h"
@@ -38,6 +38,8 @@
 #include "protos/perfetto/trace/sys_stats/sys_stats.pbzero.h"
 #include "protos/perfetto/trace/system_info.pbzero.h"
 
+#include "src/trace_processor/importers/proto/android_probes_tracker.h"
+
 namespace perfetto {
 namespace trace_processor {
 
@@ -221,26 +223,19 @@
   context_->storage->SetStats(stats::packages_list_has_parse_errors,
                               pkg_list.parse_error());
 
-  // Insert the package info into arg sets (one set per package), with the arg
-  // set ids collected in the Metadata table, under
-  // metadata::android_packages_list key type.
+  AndroidProbesTracker* tracker = AndroidProbesTracker::GetOrCreate(context_);
   for (auto it = pkg_list.packages(); it; ++it) {
-    // Insert a placeholder metadata entry, which will be overwritten by the
-    // arg_set_id when the arg tracker is flushed.
-    auto id = context_->metadata_tracker->AppendMetadata(
-        metadata::android_packages_list, Variadic::Integer(0));
-    auto add_arg = [this, id](base::StringView name, Variadic value) {
-      StringId key_id = context_->storage->InternString(name);
-      context_->args_tracker->AddArgsTo(id).AddArg(key_id, value);
-    };
     protos::pbzero::PackagesList_PackageInfo::Decoder pkg(*it);
-    add_arg("name",
-            Variadic::String(context_->storage->InternString(pkg.name())));
-    add_arg("uid", Variadic::UnsignedInteger(pkg.uid()));
-    add_arg("debuggable", Variadic::Boolean(pkg.debuggable()));
-    add_arg("profileable_from_shell",
-            Variadic::Boolean(pkg.profileable_from_shell()));
-    add_arg("version_code", Variadic::Integer(pkg.version_code()));
+    std::string pkg_name = pkg.name().ToStdString();
+    if (!tracker->ShouldInsertPackage(pkg_name)) {
+      continue;
+    }
+    context_->storage->mutable_package_list_table()->Insert(
+        {context_->storage->InternString(pkg.name()),
+         static_cast<int64_t>(pkg.uid()), pkg.debuggable(),
+         pkg.profileable_from_shell(),
+         static_cast<int64_t>(pkg.version_code())});
+    tracker->InsertedPackage(std::move(pkg_name));
   }
 }
 
diff --git a/src/trace_processor/importers/proto/android_probes_parser.h b/src/trace_processor/importers/proto/android_probes_parser.h
index 41362ed..2a85c11 100644
--- a/src/trace_processor/importers/proto/android_probes_parser.h
+++ b/src/trace_processor/importers/proto/android_probes_parser.h
@@ -20,7 +20,7 @@
 #include <vector>
 
 #include "perfetto/protozero/field.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/trace_storage.h"
 
 namespace perfetto {
 namespace trace_processor {
diff --git a/src/trace_processor/destructible.cc b/src/trace_processor/importers/proto/android_probes_tracker.cc
similarity index 72%
copy from src/trace_processor/destructible.cc
copy to src/trace_processor/importers/proto/android_probes_tracker.cc
index 22bcf6a..490ac87 100644
--- a/src/trace_processor/destructible.cc
+++ b/src/trace_processor/importers/proto/android_probes_tracker.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.
@@ -13,12 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include "src/trace_processor/destructible.h"
+
+#include "src/trace_processor/importers/proto/android_probes_tracker.h"
 
 namespace perfetto {
 namespace trace_processor {
 
-Destructible::~Destructible() = default;
+AndroidProbesTracker::AndroidProbesTracker(TraceProcessorContext*) {}
+
+AndroidProbesTracker::~AndroidProbesTracker() = default;
 
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/android_probes_tracker.h b/src/trace_processor/importers/proto/android_probes_tracker.h
new file mode 100644
index 0000000..6e2e193
--- /dev/null
+++ b/src/trace_processor/importers/proto/android_probes_tracker.h
@@ -0,0 +1,61 @@
+/*
+ * 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_IMPORTERS_PROTO_ANDROID_PROBES_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ANDROID_PROBES_TRACKER_H_
+
+#include <set>
+
+#include "perfetto/ext/base/optional.h"
+
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+class AndroidProbesTracker : public Destructible {
+ public:
+  explicit AndroidProbesTracker(TraceProcessorContext*);
+  ~AndroidProbesTracker() override;
+
+  static AndroidProbesTracker* GetOrCreate(TraceProcessorContext* context) {
+    if (!context->android_probes_tracker) {
+      context->android_probes_tracker.reset(new AndroidProbesTracker(context));
+    }
+    return static_cast<AndroidProbesTracker*>(
+        context->android_probes_tracker.get());
+  }
+
+  bool ShouldInsertPackage(const std::string& package_name) const {
+    auto it = seen_packages_.find(package_name);
+    return it == seen_packages_.end();
+  }
+
+  void InsertedPackage(std::string package_name) {
+    seen_packages_.emplace(std::move(package_name));
+  }
+
+ private:
+  std::set<std::string> seen_packages_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ANDROID_PROBES_TRACKER_H_
diff --git a/src/trace_processor/importers/proto/args_table_utils.cc b/src/trace_processor/importers/proto/args_table_utils.cc
index 0727718..508f5bf 100644
--- a/src/trace_processor/importers/proto/args_table_utils.cc
+++ b/src/trace_processor/importers/proto/args_table_utils.cc
@@ -24,13 +24,11 @@
 namespace perfetto {
 namespace trace_processor {
 
-ProtoToArgsTable::ProtoToArgsTable(
-    PacketSequenceStateGeneration* sequence_state,
-    TraceProcessorContext* context,
-    std::string starting_prefix,
-    size_t prefix_size_hint)
-    : state_{context, sequence_state}, prefix_(std::move(starting_prefix)) {
-  prefix_.reserve(prefix_size_hint);
+ProtoToArgsTable::ProtoToArgsTable(TraceProcessorContext* context)
+    : context_(context) {
+  constexpr int kDefaultSize = 64;
+  key_prefix_.reserve(kDefaultSize);
+  flat_key_prefix_.reserve(kDefaultSize);
 }
 
 util::Status ProtoToArgsTable::AddProtoFileDescriptor(
@@ -43,15 +41,21 @@
 util::Status ProtoToArgsTable::InternProtoIntoArgsTable(
     const protozero::ConstBytes& cb,
     const std::string& type,
-    ArgsTracker::BoundInserter* inserter) {
-  return InternProtoIntoArgsTableInternal(cb, type, inserter, &prefix_);
+    ArgsTracker::BoundInserter* inserter,
+    PacketSequenceStateGeneration* sequence_state,
+    const std::string& key_prefix) {
+  key_prefix_.assign(key_prefix);
+  flat_key_prefix_.assign(key_prefix);
+
+  ParsingOverrideState state{context_, sequence_state};
+  return InternProtoIntoArgsTableInternal(cb, type, inserter, state);
 }
 
 util::Status ProtoToArgsTable::InternProtoIntoArgsTableInternal(
     const protozero::ConstBytes& cb,
     const std::string& type,
     ArgsTracker::BoundInserter* inserter,
-    std::string* prefix) {
+    ParsingOverrideState state) {
   // Given |type| field the proto descriptor for this proto message.
   auto opt_proto_descriptor_idx = pool_.FindDescriptorIdx(type);
   if (!opt_proto_descriptor_idx) {
@@ -59,6 +63,10 @@
   }
   auto proto_descriptor = pool_.descriptors()[*opt_proto_descriptor_idx];
 
+  // For repeated fields, contains mapping from field descriptor index to
+  // current count of how many fields have been serialized with this field.
+  std::unordered_map<size_t, int> repeated_field_index;
+
   // Parse this message field by field until there are no bytes left.
   protozero::ProtoDecoder decoder(cb.data, cb.size);
   for (auto field = decoder.ReadField(); field.valid();
@@ -73,16 +81,29 @@
     const auto& field_descriptor =
         proto_descriptor.fields()[*opt_field_descriptor_idx];
 
+    std::string prefix_part = field_descriptor.name();
+    if (field_descriptor.is_repeated()) {
+      std::string number =
+          std::to_string(repeated_field_index[*opt_field_descriptor_idx]);
+      prefix_part.reserve(prefix_part.length() + number.length() + 2);
+      prefix_part.append("[");
+      prefix_part.append(number);
+      prefix_part.append("]");
+      repeated_field_index[*opt_field_descriptor_idx]++;
+    }
+
     // In the args table we build up message1.message2.field1 as the column
-    // name. This will append the ".field1" suffix to |prefix| and then remove
-    // it when it goes out of scope.
-    ScopedStringAppender scoped_prefix(field_descriptor.name(), prefix);
+    // name. This will append the ".field1" suffix to |key_prefix| and then
+    // remove it when it goes out of scope.
+    ScopedStringAppender scoped_prefix(prefix_part, &key_prefix_);
+    ScopedStringAppender scoped_flat_key_prefix(field_descriptor.name(),
+                                                &flat_key_prefix_);
 
     // If we have an override parser then use that instead and move onto the
     // next loop.
-    auto it = FindOverride(*prefix);
+    auto it = FindOverride(key_prefix_);
     if (it != overrides_.end()) {
-      if (it->second(state_, field, inserter)) {
+      if (it->second(state, field, inserter)) {
         continue;
       }
     }
@@ -94,14 +115,18 @@
         protos::pbzero::FieldDescriptorProto::TYPE_MESSAGE) {
       auto status = InternProtoIntoArgsTableInternal(
           field.as_bytes(), field_descriptor.resolved_type_name(), inserter,
-          prefix);
+          state);
       if (!status.ok()) {
         return status;
       }
     } else {
-      const StringId id =
-          state_.context->storage->InternString(base::StringView(*prefix));
-      inserter->AddArg(id, ConvertProtoTypeToVariadic(field_descriptor, field));
+      const StringId key_id =
+          state.context->storage->InternString(base::StringView(key_prefix_));
+      const StringId flat_key_id = state.context->storage->InternString(
+          base::StringView(flat_key_prefix_));
+      inserter->AddArg(
+          flat_key_id, key_id,
+          ConvertProtoTypeToVariadic(field_descriptor, field, state));
     }
   }
   PERFETTO_DCHECK(decoder.bytes_left() == 0);
@@ -124,7 +149,8 @@
 
 Variadic ProtoToArgsTable::ConvertProtoTypeToVariadic(
     const FieldDescriptor& descriptor,
-    const protozero::Field& field) {
+    const protozero::Field& field,
+    ParsingOverrideState state) {
   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
   switch (descriptor.type()) {
     case FieldDescriptorProto::TYPE_INT32:
@@ -151,7 +177,7 @@
       return Variadic::Real(static_cast<double>(field.as_float()));
     case FieldDescriptorProto::TYPE_STRING:
       return Variadic::String(
-          state_.context->storage->InternString(field.as_string()));
+          state.context->storage->InternString(field.as_string()));
     case FieldDescriptorProto::TYPE_ENUM: {
       auto opt_enum_descriptor_idx =
           pool_.FindDescriptorIdx(descriptor.resolved_type_name());
@@ -166,7 +192,7 @@
         // Fall back to the integer representation of the field.
         return Variadic::Integer(field.as_int32());
       }
-      return Variadic::String(state_.context->storage->InternString(
+      return Variadic::String(state.context->storage->InternString(
           base::StringView(*opt_enum_string)));
     }
     default: {
diff --git a/src/trace_processor/importers/proto/args_table_utils.h b/src/trace_processor/importers/proto/args_table_utils.h
index 9eb95b2..05f158c 100644
--- a/src/trace_processor/importers/proto/args_table_utils.h
+++ b/src/trace_processor/importers/proto/args_table_utils.h
@@ -19,16 +19,16 @@
 
 #include "perfetto/protozero/proto_decoder.h"
 #include "perfetto/trace_processor/status.h"
-#include "src/trace_processor/args_tracker.h"
-#include "src/trace_processor/descriptors.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
 #include "src/trace_processor/importers/proto/packet_sequence_state.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/util/descriptors.h"
 
 namespace perfetto {
 namespace trace_processor {
 
-// ProtoToArgsTable encapsulates the process of taking an arbitary proto and
-// assocating each field as a column in an args set. This is done by traversing
+// ProtoToArgsTable encapsulates the process of taking an arbitrary proto and
+// associating each field as a column in an args set. This is done by traversing
 // the proto using reflection (with descriptors provided by
 // AddProtoFileDescriptor()) and creating column names equal to this traversal.
 //
@@ -98,16 +98,8 @@
     std::string* str_;
   };
 
-  // |sequence_state| provides access to interning data.
   // |context| provides access to storage.
-  //
-  // |starting_prefix| will be prepended to all columns.
-  // |prefix_size_hint| allows the class to upfront reserve the expected string
-  // size needed.
-  ProtoToArgsTable(PacketSequenceStateGeneration* sequence_state,
-                   TraceProcessorContext* context,
-                   std::string starting_prefix = "",
-                   size_t prefix_size_hint = 64);
+  explicit ProtoToArgsTable(TraceProcessorContext* context);
 
   // Adds a compile time reflection of a set of proto files. You must provide
   // the descriptor before attempting to parse this with
@@ -121,7 +113,9 @@
                                       size_t proto_descriptor_array_size);
 
   // Given a view of bytes that represent a serialized protozero message of
-  // |type| we will parse each field into the Args table using RowId |row|.
+  // |type| we will parse each field into the Args table using RowId |row|,
+  // adding |key_prefix| in front of each name (can be an empty string if no
+  // prefix is needed).
   //
   // Returns on any error with a status describing the problem. However any
   // added values before encountering the error will be added to the
@@ -132,14 +126,15 @@
   // beginning. I.E. ".perfetto.protos.TrackEvent". And must match one of the
   // descriptors already added through |AddProtoFileDescriptor|.
   //
-  // IMPORTANT: currently bytes fields are not supported and repeated fields do
-  // not properly use key/flat_key in the args table (they will be equal in
-  // value).
+  // IMPORTANT: currently bytes fields are not supported.
   //
-  // TODO(b/145578432): Add support for repeated fields and byte fields.
-  util::Status InternProtoIntoArgsTable(const protozero::ConstBytes& cb,
-                                        const std::string& type,
-                                        ArgsTracker::BoundInserter* inserter);
+  // TODO(b/145578432): Add support for byte fields.
+  util::Status InternProtoIntoArgsTable(
+      const protozero::ConstBytes& cb,
+      const std::string& type,
+      ArgsTracker::BoundInserter* inserter,
+      PacketSequenceStateGeneration* sequence_state,
+      const std::string& key_prefix);
 
   // Installs an override for the field at the specified path. We will invoke
   // |parsing_override| when the field is encountered.
@@ -170,19 +165,21 @@
       const protozero::ConstBytes& cb,
       const std::string& type,
       ArgsTracker::BoundInserter* inserter,
-      std::string* prefix);
+      ParsingOverrideState state);
 
   using OverrideIterator =
       std::vector<std::pair<std::string, ParsingOverride>>::iterator;
   OverrideIterator FindOverride(const std::string& field);
 
   Variadic ConvertProtoTypeToVariadic(const FieldDescriptor& descriptor,
-                                      const protozero::Field& field);
+                                      const protozero::Field& field,
+                                      ParsingOverrideState state);
 
-  ParsingOverrideState state_;
   std::vector<std::pair<std::string, ParsingOverride>> overrides_;
   DescriptorPool pool_;
-  std::string prefix_;
+  std::string key_prefix_;
+  std::string flat_key_prefix_;
+  TraceProcessorContext* context_;
 };
 
 }  // namespace trace_processor
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 df023b4..d4c280f 100644
--- a/src/trace_processor/importers/proto/args_table_utils_unittest.cc
+++ b/src/trace_processor/importers/proto/args_table_utils_unittest.cc
@@ -23,9 +23,8 @@
 #include "protos/perfetto/trace/track_event/source_location.pbzero.h"
 #include "src/protozero/test/example_proto/test_messages.descriptor.h"
 #include "src/protozero/test/example_proto/test_messages.pbzero.h"
-#include "src/trace_processor/args_tracker.h"
-#include "src/trace_processor/importers/proto/chrome_compositor_scheduler_state.descriptor.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/storage/trace_storage.h"
 #include "test/gtest_and_gmock.h"
 
 namespace perfetto {
@@ -49,16 +48,25 @@
     sequence_state_.reset(new PacketSequenceState(&context_));
   }
 
-  bool HasArg(ArgSetId set_id, const base::StringView& key, Variadic value) {
+  /**
+   * Check whether the argument set contains the value with given flat_key and
+   * key and is equal to the given value.
+   */
+  bool HasArg(ArgSetId set_id,
+              const base::StringView& flat_key,
+              const base::StringView& key,
+              Variadic value) {
     const auto& args = storage_->arg_table();
     auto key_id = storage_->string_pool().GetId(key);
     EXPECT_TRUE(key_id);
+    auto flat_key_id = storage_->string_pool().GetId(flat_key);
+    EXPECT_TRUE(flat_key_id);
 
     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);
+        EXPECT_EQ(args.flat_key()[it.row()], flat_key_id);
         if (storage_->GetArgValue(it.row()) == value) {
           found = true;
           break;
@@ -68,26 +76,22 @@
     return found;
   }
 
+  /**
+   * Implementation of HasArg for a simple case when flat_key is equals to key,
+   * so that two won't have to be repeated for each assertion.
+   */
+  bool HasArg(ArgSetId set_id, const base::StringView& key, Variadic value) {
+    return HasArg(set_id, key, key, value);
+  }
+
   uint32_t arg_set_id_ = 1;
   std::unique_ptr<PacketSequenceState> sequence_state_;
   TraceProcessorContext context_;
   TraceStorage* storage_;
 };
 
-TEST_F(ArgsTableUtilsTest, EnsureChromeCompositorStateDescriptorParses) {
-  ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
-                          0);
-  auto status = helper.AddProtoFileDescriptor(
-      kChromeCompositorSchedulerStateDescriptor.data(),
-      kChromeCompositorSchedulerStateDescriptor.size());
-  EXPECT_TRUE(status.ok())
-      << "Failed to parse kChromeCompositorSchedulerStateDescriptor: "
-      << status.message();
-}
-
 TEST_F(ArgsTableUtilsTest, EnsureTestMessageProtoParses) {
-  ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
-                          0);
+  ProtoToArgsTable helper(&context_);
   auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
                                               kTestMessagesDescriptor.size());
   EXPECT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
@@ -123,8 +127,7 @@
   auto binary_proto = msg.SerializeAsArray();
 
   storage_->mutable_track_table()->Insert({});
-  ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
-                          0);
+  ProtoToArgsTable helper(&context_);
   auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
                                               kTestMessagesDescriptor.size());
   ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
@@ -133,7 +136,8 @@
   auto inserter = context_.args_tracker->AddArgsTo(TrackId(0));
   status = helper.InternProtoIntoArgsTable(
       protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
-      ".protozero.test.protos.EveryField", &inserter);
+      ".protozero.test.protos.EveryField", &inserter,
+      sequence_state_->current_generation(), /* key_prefix= */ "");
 
   EXPECT_TRUE(status.ok()) << "InternProtoIntoArgsTable failed with error: "
                            << status.message();
@@ -181,10 +185,14 @@
   EXPECT_TRUE(HasArg(
       ArgSetId(arg_set_id_), "field_string",
       Variadic::String(*context_.storage->string_pool().GetId("FizzBuzz"))));
-  EXPECT_TRUE(
-      HasArg(ArgSetId(arg_set_id_), "repeated_int32", Variadic::Integer(1)));
-  EXPECT_TRUE(
-      HasArg(ArgSetId(arg_set_id_), "repeated_int32", Variadic::Integer(-1)));
+  EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "repeated_int32",
+                     "repeated_int32[0]", Variadic::Integer(1)));
+  EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "repeated_int32",
+                     "repeated_int32[1]", Variadic::Integer(-1)));
+  EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "repeated_int32",
+                     "repeated_int32[2]", Variadic::Integer(100)));
+  EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "repeated_int32",
+                     "repeated_int32[3]", Variadic::Integer(2000000)));
 }
 
 TEST_F(ArgsTableUtilsTest, NestedProto) {
@@ -195,8 +203,7 @@
   auto binary_proto = msg.SerializeAsArray();
 
   storage_->mutable_track_table()->Insert({});
-  ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
-                          0);
+  ProtoToArgsTable helper(&context_);
   auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
                                               kTestMessagesDescriptor.size());
   ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
@@ -205,7 +212,8 @@
   auto inserter = context_.args_tracker->AddArgsTo(TrackId(0));
   status = helper.InternProtoIntoArgsTable(
       protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
-      ".protozero.test.protos.NestedA", &inserter);
+      ".protozero.test.protos.NestedA", &inserter,
+      sequence_state_->current_generation(), /* key_prefix= */ "");
   EXPECT_TRUE(status.ok()) << "InternProtoIntoArgsTable failed with error: "
                            << status.message();
   context_.args_tracker->Flush();
@@ -223,8 +231,7 @@
   auto binary_proto = msg.SerializeAsArray();
 
   storage_->mutable_track_table()->Insert({});
-  ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
-                          0);
+  ProtoToArgsTable helper(&context_);
   auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
                                               kTestMessagesDescriptor.size());
   ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
@@ -233,7 +240,8 @@
   auto inserter = context_.args_tracker->AddArgsTo(TrackId(0));
   status = helper.InternProtoIntoArgsTable(
       protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
-      ".protozero.test.protos.CamelCaseFields", &inserter);
+      ".protozero.test.protos.CamelCaseFields", &inserter,
+      sequence_state_->current_generation(), /* key_prefix= */ "");
   EXPECT_TRUE(status.ok()) << "InternProtoIntoArgsTable failed with error: "
                            << status.message();
   context_.args_tracker->Flush();
@@ -251,8 +259,7 @@
   auto binary_proto = msg.SerializeAsArray();
 
   storage_->mutable_track_table()->Insert({});
-  ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
-                          0);
+  ProtoToArgsTable helper(&context_);
   auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
                                               kTestMessagesDescriptor.size());
   ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
@@ -276,7 +283,8 @@
   auto inserter = context_.args_tracker->AddArgsTo(TrackId(0));
   status = helper.InternProtoIntoArgsTable(
       protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
-      ".protozero.test.protos.NestedA", &inserter);
+      ".protozero.test.protos.NestedA", &inserter,
+      sequence_state_->current_generation(), /* key_prefix= */ "");
   EXPECT_TRUE(status.ok()) << "InternProtoIntoArgsTable failed with error: "
                            << status.message();
   context_.args_tracker->Flush();
@@ -292,8 +300,7 @@
   auto binary_proto = msg.SerializeAsArray();
 
   storage_->mutable_track_table()->Insert({});
-  ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
-                          0);
+  ProtoToArgsTable helper(&context_);
   auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
                                               kTestMessagesDescriptor.size());
   ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
@@ -316,7 +323,8 @@
   auto inserter = context_.args_tracker->AddArgsTo(TrackId(0));
   status = helper.InternProtoIntoArgsTable(
       protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
-      ".protozero.test.protos.NestedA", &inserter);
+      ".protozero.test.protos.NestedA", &inserter,
+      sequence_state_->current_generation(), /* key_prefix= */ "");
   EXPECT_TRUE(status.ok()) << "InternProtoIntoArgsTable failed with error: "
                            << status.message();
   context_.args_tracker->Flush();
@@ -347,8 +355,7 @@
       protos::pbzero::InternedData::kSourceLocationsFieldNumber,
       std::move(blob));
 
-  ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
-                          0);
+  ProtoToArgsTable helper(&context_);
   // Now we override the behaviour of |value_c| so we can expand the iid into
   // multiple args rows.
   helper.AddParsingOverride(
@@ -381,7 +388,8 @@
   auto inserter = context_.args_tracker->AddArgsTo(TrackId(0));
   status = helper.InternProtoIntoArgsTable(
       protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
-      ".protozero.test.protos.NestedA", &inserter);
+      ".protozero.test.protos.NestedA", &inserter,
+      sequence_state_->current_generation(), /* key_prefix= */ "");
   EXPECT_TRUE(status.ok()) << "InternProtoIntoArgsTable failed with error: "
                            << status.message();
   auto file_name_id = storage_->string_pool().GetId("test_file_name");
@@ -393,6 +401,43 @@
       HasArg(ArgSetId(arg_set_id_), "line_number", Variadic::Integer(2)));
 }
 
+TEST_F(ArgsTableUtilsTest, NonEmptyPrefix) {
+  using namespace protozero::test::protos::pbzero;
+  protozero::HeapBuffered<EveryField> msg{kChunkSize, kChunkSize};
+  msg->add_repeated_int32(1);
+  msg->add_repeated_int32(-1);
+  msg->add_repeated_int32(100);
+  msg->add_repeated_int32(2000000);
+
+  auto binary_proto = msg.SerializeAsArray();
+
+  storage_->mutable_track_table()->Insert({});
+  ProtoToArgsTable helper(&context_);
+  auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
+                                              kTestMessagesDescriptor.size());
+  ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
+                           << status.message();
+
+  auto inserter = context_.args_tracker->AddArgsTo(TrackId(0));
+  status = helper.InternProtoIntoArgsTable(
+      protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
+      ".protozero.test.protos.EveryField", &inserter,
+      sequence_state_->current_generation(), /* key_prefix= */ "prefix");
+
+  EXPECT_TRUE(status.ok()) << "InternProtoIntoArgsTable failed with error: "
+                           << status.message();
+
+  context_.args_tracker->Flush();
+  EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "prefix.repeated_int32",
+                     "prefix.repeated_int32[0]", Variadic::Integer(1)));
+  EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "prefix.repeated_int32",
+                     "prefix.repeated_int32[1]", Variadic::Integer(-1)));
+  EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "prefix.repeated_int32",
+                     "prefix.repeated_int32[2]", Variadic::Integer(100)));
+  EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "prefix.repeated_int32",
+                     "prefix.repeated_int32[3]", Variadic::Integer(2000000)));
+}
+
 }  // namespace
 }  // namespace trace_processor
 }  // namespace perfetto
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
deleted file mode 100644
index adc3f2d..0000000
--- a/src/trace_processor/importers/proto/chrome_compositor_scheduler_state.descriptor.h
+++ /dev/null
@@ -1,885 +0,0 @@
-/*
- * 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_IMPORTERS_PROTO_CHROME_COMPOSITOR_SCHEDULER_STATE_DESCRIPTOR_H_
-#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_CHROME_COMPOSITOR_SCHEDULER_STATE_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/trace/track_event/chrome_compositor_scheduler_state.proto)
-// 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, 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,
-     0x63, 0x61, 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, 0x85, 0x01, 0x0a, 0x0e, 0x53, 0x6f,
-     0x75, 0x72, 0x63, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
-     0x12, 0x10, 0x0a, 0x03, 0x69, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
-     0x04, 0x52, 0x03, 0x69, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69,
-     0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
-     0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12,
-     0x23, 0x0a, 0x0d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
-     0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
-     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, 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,
-     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, 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,
-     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, 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, 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, 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, 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, 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,
-     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, 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, 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
-
-#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_CHROME_COMPOSITOR_SCHEDULER_STATE_DESCRIPTOR_H_
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 eb58594..cff29b8 100644
--- a/src/trace_processor/importers/proto/graphics_event_parser.cc
+++ b/src/trace_processor/importers/proto/graphics_event_parser.cc
@@ -20,13 +20,13 @@
 
 #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 "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 #include "protos/perfetto/common/gpu_counter_descriptor.pbzero.h"
 #include "protos/perfetto/trace/android/graphics_frame_event.pbzero.h"
@@ -98,10 +98,6 @@
       description_id_(context->storage->InternString("description")),
       gpu_render_stage_scope_id_(
           context->storage->InternString("gpu_render_stage")),
-      command_buffer_handle_id_(
-          context->storage->InternString("VkCommandBuffer")),
-      render_target_handle_id_(context->storage->InternString("VkFramebuffer")),
-      render_pass_handle_id_(context->storage->InternString("VkRenderPass")),
       graphics_event_scope_id_(
           context->storage->InternString("graphics_frame_event")),
       unknown_event_name_id_(context->storage->InternString("unknown_event")),
@@ -128,11 +124,12 @@
            context->storage->InternString("Detach") /* DETACH */,
            context->storage->InternString("Attach") /* ATTACH */,
            context->storage->InternString("Cancel") /* CANCEL */}},
-      present_frame_name_(
-          base::StringWriter(present_frame_, sizeof(present_frame_))),
-      present_frame_layer_name_(
-          base::StringWriter(present_frame_layer_,
-                             sizeof(present_frame_layer_))),
+      present_frame_name_(present_frame_buffer_,
+                          base::ArraySize(present_frame_buffer_)),
+      present_frame_layer_name_(present_frame_layer_buffer_,
+                                base::ArraySize(present_frame_layer_buffer_)),
+      present_frame_numbers_(present_frame_numbers_buffer_,
+                             base::ArraySize(present_frame_numbers_buffer_)),
       gpu_log_track_name_id_(context_->storage->InternString("GPU Log")),
       gpu_log_scope_id_(context_->storage->InternString("gpu_log")),
       tag_id_(context_->storage->InternString("tag")),
@@ -144,7 +141,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);
@@ -245,25 +245,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 debug_marker_name =
-        FindDebugName(VK_OBJECT_TYPE_FRAMEBUFFER, event.render_target_handle());
-    str_writer.AppendChar('[');
-    if (debug_marker_name.has_value()) {
-      str_writer.AppendString(base::StringView(debug_marker_name.value()));
-    } else {
-      str_writer.AppendLiteral("0x");
-      str_writer.AppendHexInt(event.render_target_handle());
-    }
-    str_writer.AppendChar(']');
-    stage_name = context_->storage->InternString(str_writer.GetStringView());
-  }
   return stage_name;
 }
 
@@ -328,23 +309,6 @@
   }
 }
 
-void GraphicsEventParser::AddVulkanHandleArg(
-    ArgsTracker::BoundInserter* inserter,
-    StringId key,
-    int32_t vk_object_type,
-    uint64_t vk_handle) {
-  char buf[256];
-  auto debug_marker_name = FindDebugName(vk_object_type, vk_handle);
-  if (debug_marker_name.has_value()) {
-    snprintf(buf, base::ArraySize(buf), "0x%016" PRIx64 " (%s)", vk_handle,
-             debug_marker_name.value().c_str());
-  } else {
-    snprintf(buf, base::ArraySize(buf), "0x%016" PRIx64, vk_handle);
-  }
-  StringId value = context_->storage->InternString(buf);
-  inserter->AddArg(key, Variadic::String(value));
-}
-
 void GraphicsEventParser::ParseGpuRenderStageEvent(int64_t ts,
                                                    ConstBytes blob) {
   protos::pbzero::GpuRenderStageEvent::Decoder event(blob.data, blob.size);
@@ -378,21 +342,6 @@
         inserter->AddArg(description_id_, Variadic::String(description));
       }
     }
-    if (event.has_command_buffer_handle()) {
-      AddVulkanHandleArg(inserter, command_buffer_handle_id_,
-                         VK_OBJECT_TYPE_COMMAND_BUFFER,
-                         event.command_buffer_handle());
-    }
-    if (event.has_render_target_handle()) {
-      AddVulkanHandleArg(inserter, render_target_handle_id_,
-                         VK_OBJECT_TYPE_FRAMEBUFFER,
-                         event.render_target_handle());
-    }
-    if (event.has_render_pass_handle()) {
-      AddVulkanHandleArg(inserter, render_pass_handle_id_,
-                         VK_OBJECT_TYPE_RENDER_PASS,
-                         event.render_pass_handle());
-    }
     for (auto it = event.extra_data(); it; ++it) {
       protos::pbzero::GpuRenderStageEvent_ExtraData_Decoder datum(*it);
       StringId name_id = context_->storage->InternString(datum.name());
@@ -433,15 +382,34 @@
       gpu_hw_queue_ids_[hw_queue_id] = track_id;
     }
 
+    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;
+
     tables::GpuSliceTable::Row row;
     row.ts = ts;
-    row.track_id = track_id.value;
+    row.track_id = track_id;
     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;
 
@@ -451,6 +419,7 @@
 
 void GraphicsEventParser::ParseGraphicsFrameEvent(int64_t timestamp,
                                                   ConstBytes blob) {
+  using GraphicsFrameEvent = protos::pbzero::GraphicsFrameEvent;
   protos::pbzero::GraphicsFrameEvent_Decoder frame_event(blob.data, blob.size);
   if (!frame_event.has_buffer_event()) {
     return;
@@ -472,6 +441,7 @@
     const auto type = static_cast<size_t>(event.type());
     if (type < event_type_name_ids_.size()) {
       event_name_id = event_type_name_ids_[type];
+      graphics_frame_stats_map_[event.buffer_id()][type] = timestamp;
     } else {
       context_->storage->IncrementStats(
           stats::graphics_frame_event_parser_errors);
@@ -513,22 +483,42 @@
   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));
-        };
+    char frame_number_buffer[256];
+    base::StringWriter frame_numbers(frame_number_buffer,
+                                     base::ArraySize(frame_number_buffer));
+    frame_numbers.AppendUnsignedInt(frame_number);
 
-    tables::GpuSliceTable::Row row;
+    tables::GraphicsFrameSliceTable::Row row;
     row.ts = timestamp;
-    row.track_id = track_id.value;
+    row.track_id = track_id;
     row.name = event_name_id;
     row.dur = duration;
-    row.frame_id = frame_number;
-    context_->slice_tracker->ScopedGpu(row, scoped_callback);
+    row.frame_numbers =
+        context_->storage->InternString(frame_numbers.GetStringView());
+    row.layer_names = layer_name_id;
+    context_->slice_tracker->ScopedFrameEvent(row);
   }
 
   /* Displayed Frame track */
-  if (event.type() == protos::pbzero::GraphicsFrameEvent::PRESENT_FENCE) {
+  if (event.type() == GraphicsFrameEvent::PRESENT_FENCE) {
+    // Insert the frame stats for the buffer that was presented
+    auto acquire_ts =
+        graphics_frame_stats_map_[event.buffer_id()]
+                                 [GraphicsFrameEvent::ACQUIRE_FENCE];
+    auto queue_ts =
+        graphics_frame_stats_map_[event.buffer_id()][GraphicsFrameEvent::QUEUE];
+    auto latch_ts =
+        graphics_frame_stats_map_[event.buffer_id()][GraphicsFrameEvent::LATCH];
+    tables::GraphicsFrameStatsTable::Row stats_row;
+    // AcquireFence can signal before Queue sometimes, so have 0 as a bound.
+    stats_row.queue_to_acquire_time =
+        std::max(acquire_ts - queue_ts, static_cast<int64_t>(0));
+    stats_row.acquire_to_latch_time = latch_ts - acquire_ts;
+    stats_row.latch_to_present_time = timestamp - latch_ts;
+    auto stats_row_id =
+        context_->storage->mutable_graphics_frame_stats_table()->Insert(
+            stats_row);
+
     if (previous_timestamp_ == 0) {
       const StringId present_track_name_id =
           context_->storage->InternString("Displayed Frame");
@@ -550,51 +540,75 @@
       present_frame_name_.AppendLiteral(", ");
       present_frame_name_.AppendUnsignedInt(buffer_id);
 
-      // Layer name is added as args while finishing the slice
+      // Append Layer names
       present_frame_layer_name_.AppendLiteral(", ");
       present_frame_layer_name_.AppendString(event.layer_name());
+
+      // Append Frame numbers
+      present_frame_numbers_.AppendLiteral(", ");
+      present_frame_numbers_.AppendUnsignedInt(frame_number);
+
+      // Add the current stats row to the list of stats that go with this frame
+      graphics_frame_stats_idx_.push_back(stats_row_id.row);
     } else {
 
       if (previous_timestamp_ != 0) {
         StringId present_frame_layer_name_id = context_->storage->InternString(
             present_frame_layer_name_.GetStringView());
-        auto args_callback = [this, present_frame_layer_name_id](
-                                 ArgsTracker::BoundInserter* inserter) {
-          inserter->AddArg(layer_name_key_id_,
-                           Variadic::String(present_frame_layer_name_id));
-        };
         // End the current slice that's being tracked.
-        const auto opt_slice_id = context_->slice_tracker->EndGpu(
-            timestamp, present_track_id_, args_callback);
+        const auto opt_slice_id = context_->slice_tracker->EndFrameEvent(
+            timestamp, present_track_id_);
 
         if (opt_slice_id) {
           // The slice could have had additional buffers in it, so we need to
-          // update the name.
-          auto* slice_table = context_->storage->mutable_slice_table();
+          // update the table.
+          auto* graphics_frame_slice_table =
+              context_->storage->mutable_graphics_frame_slice_table();
 
-          uint32_t row_idx = *slice_table->id().IndexOf(*opt_slice_id);
+          uint32_t row_idx =
+              *graphics_frame_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);
-        }
+          graphics_frame_slice_table->mutable_name()->Set(row_idx,
+                                                          frame_name_id);
 
+          StringId present_frame_numbers_id = context_->storage->InternString(
+              present_frame_numbers_.GetStringView());
+          graphics_frame_slice_table->mutable_frame_numbers()->Set(
+              row_idx, present_frame_numbers_id);
+          graphics_frame_slice_table->mutable_layer_names()->Set(
+              row_idx, present_frame_layer_name_id);
+
+          // Set the slice_id for the frame_stats rows under this displayed
+          // frame
+          auto* slice_table = context_->storage->mutable_slice_table();
+          uint32_t slice_idx = *slice_table->id().IndexOf(*opt_slice_id);
+          for (uint32_t i = 0; i < graphics_frame_stats_idx_.size(); i++) {
+            context_->storage->mutable_graphics_frame_stats_table()
+                ->mutable_slice_id()
+                ->Set(graphics_frame_stats_idx_[i], slice_idx);
+          }
+        }
         present_frame_layer_name_.reset();
         present_frame_name_.reset();
+        present_frame_numbers_.reset();
+        graphics_frame_stats_idx_.clear();
       }
 
       // Start a new slice
       present_frame_name_.AppendUnsignedInt(buffer_id);
       previous_timestamp_ = timestamp;
       present_frame_layer_name_.AppendString(event.layer_name());
+      present_frame_numbers_.AppendUnsignedInt(frame_number);
       present_event_name_id_ =
           context_->storage->InternString(present_frame_name_.GetStringView());
+      graphics_frame_stats_idx_.push_back(stats_row_id.row);
 
-      tables::GpuSliceTable::Row row;
+      tables::GraphicsFrameSliceTable::Row row;
       row.ts = timestamp;
-      row.track_id = present_track_id_.value;
+      row.track_id = present_track_id_;
       row.name = present_event_name_id_;
-      row.frame_id = frame_number;
-      context_->slice_tracker->BeginGpu(row);
+      context_->slice_tracker->BeginFrameEvent(row);
     }
   }
 }
@@ -746,7 +760,7 @@
                                        vulkan_memory_event);
 
   auto* allocs = context_->storage->mutable_vulkan_memory_allocations_table();
-  VulkanAllocId id = allocs->Insert(vulkan_memory_event_row);
+  VulkanAllocId id = allocs->Insert(vulkan_memory_event_row).id;
 
   if (vulkan_memory_event.has_annotations()) {
     auto inserter = context_->args_tracker->AddArgsTo(id);
@@ -804,13 +818,13 @@
 
   tables::GpuSliceTable::Row row;
   row.ts = ts;
-  row.track_id = track_id.value;
+  row.track_id = track_id;
   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(
@@ -818,6 +832,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;
+    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 c2bae51..93224d4 100644
--- a/src/trace_processor/importers/proto/graphics_event_parser.h
+++ b/src/trace_processor/importers/proto/graphics_event_parser.h
@@ -20,12 +20,13 @@
 #include <vector>
 
 #include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/string_writer.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/common/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"
+#include "src/trace_processor/storage/trace_storage.h"
 
 #include "protos/perfetto/trace/gpu/vulkan_memory_event.pbzero.h"
 
@@ -67,7 +68,7 @@
   void UpdateVulkanMemoryAllocationCounters(UniquePid,
                                             const VulkanMemoryEvent::Decoder&);
 
-  void ParseVulkanApiEvent(ConstBytes);
+  void ParseVulkanApiEvent(int64_t, ConstBytes);
 
  private:
   const StringId GetFullStageName(
@@ -77,10 +78,6 @@
           GpuRenderStageEvent_Specifications_Description_Decoder& hw_queue);
   base::Optional<std::string> FindDebugName(int32_t vk_object_type,
                                             uint64_t vk_handle) const;
-  void AddVulkanHandleArg(ArgsTracker::BoundInserter* inserter,
-                          StringId key,
-                          int32_t vk_object_type,
-                          uint64_t vk_handle);
 
   TraceProcessorContext* const context_;
   VulkanMemoryTracker vulkan_memory_tracker_;
@@ -91,9 +88,6 @@
   const StringId gpu_render_stage_scope_id_;
   std::vector<perfetto::base::Optional<TrackId>> gpu_hw_queue_ids_;
   size_t gpu_hw_queue_counter_ = 0;
-  const StringId command_buffer_handle_id_;
-  const StringId render_target_handle_id_;
-  const StringId render_pass_handle_id_;
   // Map of stage ID -> pair(stage name, stage description)
   std::vector<std::pair<StringId, StringId>> gpu_render_stage_ids_;
   // For GraphicsFrameEvent
@@ -103,12 +97,20 @@
   const StringId layer_name_key_id_;
   std::array<StringId, 14> event_type_name_ids_;
   int64_t previous_timestamp_ = 0;
-  char present_frame_[4096];
-  char present_frame_layer_[4096];
+  char present_frame_buffer_[4096];
+  char present_frame_layer_buffer_[4096];
+  char present_frame_numbers_buffer_[4096];
   StringId present_event_name_id_;
   base::StringWriter present_frame_name_;
   base::StringWriter present_frame_layer_name_;
+  base::StringWriter present_frame_numbers_;
   TrackId present_track_id_;
+  // Row indices of frame stats table. Used to populate the slice_id after
+  // inserting the rows.
+  std::vector<uint32_t> graphics_frame_stats_idx_;
+  // Map of buffer ID -> (Map of GraphicsFrameEvent -> ts of that event)
+  std::unordered_map<uint32_t, std::unordered_map<uint64_t, int64_t>>
+      graphics_frame_stats_map_;
   // For VulkanMemoryEvent
   std::unordered_map<VulkanMemoryEvent::AllocationScope,
                      int64_t /*counter_value*/,
@@ -125,10 +127,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 c75a340..102473b 100644
--- a/src/trace_processor/importers/proto/heap_graph_module.cc
+++ b/src/trace_processor/importers/proto/heap_graph_module.cc
@@ -16,10 +16,10 @@
 
 #include "src/trace_processor/importers/proto/heap_graph_module.h"
 
+#include "src/trace_processor/importers/common/process_tracker.h"
 #include "src/trace_processor/importers/proto/heap_graph_tracker.h"
-#include "src/trace_processor/process_tracker.h"
-#include "src/trace_processor/trace_processor_context.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 #include "protos/perfetto/trace/profiling/heap_graph.pbzero.h"
 
@@ -159,6 +159,15 @@
     }
     heap_graph_tracker->AddObject(seq_id, upid, ts, std::move(obj));
   }
+  for (auto it = heap_graph.types(); it; ++it) {
+    protos::pbzero::HeapGraphType::Decoder entry(*it);
+    const char* str = reinterpret_cast<const char*>(entry.class_name().data);
+    auto str_view = base::StringView(str, entry.class_name().size);
+
+    heap_graph_tracker->AddInternedType(
+        seq_id, entry.id(), context_->storage->InternString(str_view),
+        entry.location_id());
+  }
   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);
@@ -172,7 +181,14 @@
     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(), str_view);
+  }
+  for (auto it = heap_graph.location_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->AddInternedLocationName(
         seq_id, entry.iid(), context_->storage->InternString(str_view));
   }
   for (auto it = heap_graph.roots(); it; ++it) {
@@ -199,11 +215,50 @@
   }
 }
 
+void HeapGraphModule::DeobfuscateClass(
+    base::Optional<StringPool::Id> package_name_id,
+    StringPool::Id obfuscated_class_name_id,
+    const protos::pbzero::ObfuscatedClass::Decoder& cls) {
+  auto* heap_graph_tracker = HeapGraphTracker::GetOrCreate(context_);
+  const std::vector<tables::HeapGraphClassTable::Id>* cls_objects =
+      heap_graph_tracker->RowsForType(package_name_id,
+                                      obfuscated_class_name_id);
+
+  if (cls_objects) {
+    heap_graph_tracker->AddDeobfuscationMapping(
+        package_name_id, obfuscated_class_name_id,
+        context_->storage->InternString(
+            base::StringView(cls.deobfuscated_name())));
+
+    for (tables::HeapGraphClassTable::Id id : *cls_objects) {
+      uint32_t row =
+          *context_->storage->heap_graph_class_table().id().IndexOf(id);
+      const StringPool::Id obfuscated_type_name =
+          context_->storage->heap_graph_class_table().name()[row];
+      StringPool::Id deobfuscated_type_name =
+          heap_graph_tracker->MaybeDeobfuscate(package_name_id,
+                                               obfuscated_type_name);
+      PERFETTO_CHECK(!deobfuscated_type_name.is_null());
+      context_->storage->mutable_heap_graph_class_table()
+          ->mutable_deobfuscated_name()
+          ->Set(row, deobfuscated_type_name);
+    }
+  } else {
+    PERFETTO_DLOG("Class %s not found",
+                  cls.obfuscated_name().ToStdString().c_str());
+  }
+}
+
 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);
+  base::Optional<StringPool::Id> package_name_id;
+  if (deobfuscation_mapping.package_name().size > 0) {
+    package_name_id = context_->storage->string_pool().GetId(
+        deobfuscation_mapping.package_name());
+  }
+
   for (auto class_it = deobfuscation_mapping.obfuscated_classes(); class_it;
        ++class_it) {
     protos::pbzero::ObfuscatedClass::Decoder cls(*class_it);
@@ -213,20 +268,12 @@
       PERFETTO_DLOG("Class string %s not found",
                     cls.obfuscated_name().ToStdString().c_str());
     } else {
-      const std::vector<int64_t>* cls_objects =
-          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) {
-          context_->storage->mutable_heap_graph_object_table()
-              ->mutable_deobfuscated_type_name()
-              ->Set(static_cast<uint32_t>(row), interned_deobfuscated_name);
-        }
-      } else {
-        PERFETTO_DLOG("Class %s not found",
-                      cls.obfuscated_name().ToStdString().c_str());
+      // TODO(b/153552977): Remove this work-around for legacy traces.
+      // For traces without location information, deobfuscate all matching
+      // classes.
+      DeobfuscateClass(base::nullopt, *obfuscated_class_name_id, cls);
+      if (package_name_id) {
+        DeobfuscateClass(package_name_id, *obfuscated_class_name_id, cls);
       }
     }
     for (auto member_it = cls.obfuscated_members(); member_it; ++member_it) {
@@ -235,9 +282,17 @@
       std::string merged_obfuscated = cls.obfuscated_name().ToStdString() +
                                       "." +
                                       member.obfuscated_name().ToStdString();
-      std::string merged_deobfuscated =
-          cls.deobfuscated_name().ToStdString() + "." +
+      std::string merged_deobfuscated;
+      std::string member_deobfuscated_name =
           member.deobfuscated_name().ToStdString();
+      if (member_deobfuscated_name.find('.') == std::string::npos) {
+        // Name relative to class.
+        merged_deobfuscated = cls.deobfuscated_name().ToStdString() + "." +
+                              member_deobfuscated_name;
+      } else {
+        // Fully qualified name.
+        merged_deobfuscated = std::move(member_deobfuscated_name);
+      }
 
       auto obfuscated_field_name_id = context_->storage->string_pool().GetId(
           base::StringView(merged_obfuscated));
@@ -263,5 +318,10 @@
   }
 }
 
+void HeapGraphModule::NotifyEndOfFile() {
+  auto* heap_graph_tracker = HeapGraphTracker::GetOrCreate(context_);
+  heap_graph_tracker->NotifyEndOfFile();
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/heap_graph_module.h b/src/trace_processor/importers/proto/heap_graph_module.h
index 2fde80e..b1816b0 100644
--- a/src/trace_processor/importers/proto/heap_graph_module.h
+++ b/src/trace_processor/importers/proto/heap_graph_module.h
@@ -35,9 +35,14 @@
                    const TimestampedTracePiece& ttp,
                    uint32_t field_id) override;
 
+  void NotifyEndOfFile() override;
+
  private:
   void ParseHeapGraph(uint32_t seq_id, int64_t ts, protozero::ConstBytes);
   void ParseDeobfuscationMapping(protozero::ConstBytes);
+  void DeobfuscateClass(base::Optional<StringPool::Id> package_name_id,
+                        StringPool::Id obfuscated_class_id,
+                        const protos::pbzero::ObfuscatedClass::Decoder& cls);
 
   TraceProcessorContext* context_;
 };
diff --git a/src/trace_processor/importers/proto/heap_graph_tracker.cc b/src/trace_processor/importers/proto/heap_graph_tracker.cc
index f96cf6e..2e43ae8 100644
--- a/src/trace_processor/importers/proto/heap_graph_tracker.cc
+++ b/src/trace_processor/importers/proto/heap_graph_tracker.cc
@@ -16,12 +16,171 @@
 
 #include "src/trace_processor/importers/proto/heap_graph_tracker.h"
 
+#include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/ext/base/string_utils.h"
+
 namespace perfetto {
 namespace trace_processor {
 
+namespace {
+base::Optional<base::StringView> PackageFromApp(base::StringView location) {
+  location = location.substr(base::StringView("/data/app/").size());
+  size_t slash = location.find('/');
+  if (slash == std::string::npos) {
+    return base::nullopt;
+  }
+  size_t second_slash = location.find('/', slash + 1);
+  if (second_slash == std::string::npos) {
+    return base::nullopt;
+  }
+  location = location.substr(slash + 1, second_slash - slash);
+  size_t minus = location.find('-');
+  if (minus == std::string::npos) {
+    return base::nullopt;
+  }
+  return location.substr(0, minus);
+}
+}  // namespace
+
+base::Optional<base::StringView> GetStaticClassTypeName(base::StringView type) {
+  static const base::StringView kJavaClassTemplate("java.lang.Class<");
+  if (!type.empty() && type.at(type.size() - 1) == '>' &&
+      type.substr(0, kJavaClassTemplate.size()) == kJavaClassTemplate) {
+    return type.substr(kJavaClassTemplate.size(),
+                       type.size() - kJavaClassTemplate.size() - 1);
+  }
+  return {};
+}
+
+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;
+}
+
+NormalizedType GetNormalizedType(base::StringView type) {
+  auto static_class_type_name = GetStaticClassTypeName(type);
+  if (static_class_type_name.has_value()) {
+    type = static_class_type_name.value();
+  }
+  size_t number_of_arrays = NumberOfArrays(type);
+  return {base::StringView(type.data(), type.size() - number_of_arrays * 2),
+          static_class_type_name.has_value(), number_of_arrays};
+}
+
+base::StringView NormalizeTypeName(base::StringView type) {
+  return GetNormalizedType(type).name;
+}
+
+std::string DenormalizeTypeName(NormalizedType normalized,
+                                base::StringView deobfuscated_type_name) {
+  std::string result = deobfuscated_type_name.ToStdString();
+  for (size_t i = 0; i < normalized.number_of_arrays; ++i) {
+    result += "[]";
+  }
+  if (normalized.is_static_class) {
+    result = "java.lang.Class<" + result + ">";
+  }
+  return result;
+}
+
 HeapGraphTracker::HeapGraphTracker(TraceProcessorContext* context)
     : context_(context) {}
 
+base::Optional<std::string> HeapGraphTracker::PackageFromLocation(
+    base::StringView location) {
+  // List of some hardcoded apps that do not follow the scheme used in
+  // PackageFromApp. Ask for yours to be added.
+  //
+  // TODO(b/153632336): Get rid of the hardcoded list of system apps.
+  base::StringView sysui(
+      "/system_ext/priv-app/SystemUIGoogle/SystemUIGoogle.apk");
+  if (location.size() >= sysui.size() &&
+      location.substr(0, sysui.size()) == sysui) {
+    return "com.android.systemui";
+  }
+
+  base::StringView phonesky("/product/priv-app/Phonesky/Phonesky.apk");
+  if (location.size() >= phonesky.size() &&
+      location.substr(0, phonesky.size()) == phonesky) {
+    return "com.android.vending";
+  }
+
+  base::StringView maps("/product/app/Maps/Maps.apk");
+  if (location.size() >= maps.size() &&
+      location.substr(0, maps.size()) == maps) {
+    return "com.google.android.apps.maps";
+  }
+
+  base::StringView launcher(
+      "/system_ext/priv-app/NexusLauncherRelease/NexusLauncherRelease.apk");
+  if (location.size() >= launcher.size() &&
+      location.substr(0, launcher.size()) == launcher) {
+    return "com.google.android.apps.nexuslauncher";
+  }
+
+  base::StringView photos("/product/app/Photos/Photos.apk");
+  if (location.size() >= photos.size() &&
+      location.substr(0, photos.size()) == photos) {
+    return "com.google.android.apps.photos";
+  }
+
+  base::StringView wellbeing(
+      "/product/priv-app/WellbeingPrebuilt/WellbeingPrebuilt.apk");
+  if (location.size() >= wellbeing.size() &&
+      location.substr(0, wellbeing.size()) == wellbeing) {
+    return "com.google.android.apps.wellbeing";
+  }
+
+  base::StringView matchmaker("MatchMaker");
+  if (location.size() >= matchmaker.size() &&
+      location.find(matchmaker) != base::StringView::npos) {
+    return "com.google.android.as";
+  }
+
+  base::StringView gm("/product/app/PrebuiltGmail/PrebuiltGmail.apk");
+  if (location.size() >= gm.size() && location.substr(0, gm.size()) == gm) {
+    return "com.google.android.gm";
+  }
+
+  base::StringView gmscore("/product/priv-app/PrebuiltGmsCore/PrebuiltGmsCore");
+  if (location.size() >= gmscore.size() &&
+      location.substr(0, gmscore.size()) == gmscore) {
+    return "com.google.android.gms";
+  }
+
+  base::StringView velvet("/product/priv-app/Velvet/Velvet.apk");
+  if (location.size() >= velvet.size() &&
+      location.substr(0, velvet.size()) == velvet) {
+    return "com.google.android.googlequicksearchbox";
+  }
+
+  base::StringView inputmethod(
+      "/product/app/LatinIMEGooglePrebuilt/LatinIMEGooglePrebuilt.apk");
+  if (location.size() >= inputmethod.size() &&
+      location.substr(0, inputmethod.size()) == inputmethod) {
+    return "com.google.android.inputmethod.latin";
+  }
+
+  base::StringView data_app("/data/app/");
+  if (location.substr(0, data_app.size()) == data_app) {
+    auto package = PackageFromApp(location);
+    if (!package) {
+      context_->storage->IncrementStats(stats::heap_graph_location_parse_error);
+      return base::nullopt;
+    }
+    return package->ToStdString();
+  }
+  return base::nullopt;
+}
+
 HeapGraphTracker::SequenceState& HeapGraphTracker::GetOrCreateSequence(
     uint32_t seq_id) {
   auto seq_it = sequence_state_.find(seq_id);
@@ -71,26 +230,64 @@
   sequence_state.current_roots.emplace_back(std::move(root));
 }
 
+void HeapGraphTracker::AddInternedLocationName(uint32_t seq_id,
+                                               uint64_t intern_id,
+                                               StringPool::Id strid) {
+  SequenceState& sequence_state = GetOrCreateSequence(seq_id);
+  sequence_state.interned_location_names.emplace(intern_id, strid);
+}
+
 void HeapGraphTracker::AddInternedTypeName(uint32_t seq_id,
                                            uint64_t intern_id,
                                            StringPool::Id strid) {
   SequenceState& sequence_state = GetOrCreateSequence(seq_id);
-  sequence_state.interned_type_names.emplace(intern_id, strid);
+  sequence_state.interned_types[intern_id].name = strid;
+}
+
+void HeapGraphTracker::AddInternedType(uint32_t seq_id,
+                                       uint64_t intern_id,
+                                       StringPool::Id strid,
+                                       uint64_t location_id) {
+  SequenceState& sequence_state = GetOrCreateSequence(seq_id);
+  sequence_state.interned_types[intern_id].name = strid;
+  sequence_state.interned_types[intern_id].location_id = location_id;
 }
 
 void HeapGraphTracker::AddInternedFieldName(uint32_t seq_id,
                                             uint64_t intern_id,
-                                            StringPool::Id strid) {
+                                            base::StringView str) {
   SequenceState& sequence_state = GetOrCreateSequence(seq_id);
-  sequence_state.interned_field_names.emplace(intern_id, strid);
+  size_t space = str.find(' ');
+  base::StringView type_name;
+  if (space != base::StringView::npos) {
+    type_name = str.substr(0, space);
+    str = str.substr(space + 1);
+  }
+  sequence_state.interned_fields.emplace(
+      intern_id, InternedField{context_->storage->InternString(str),
+                               context_->storage->InternString(type_name)});
 }
 
 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));
@@ -100,37 +297,100 @@
 
 void HeapGraphTracker::FinalizeProfile(uint32_t seq_id) {
   SequenceState& sequence_state = GetOrCreateSequence(seq_id);
+
+  std::map<uint64_t, tables::HeapGraphClassTable::Id> type_id_to_db;
+  for (const auto& p : sequence_state.interned_types) {
+    uint64_t id = p.first;
+    const InternedType& interned_type = p.second;
+    base::Optional<StringPool::Id> location_name;
+    if (interned_type.location_id) {
+      auto it = sequence_state.interned_location_names.find(
+          *interned_type.location_id);
+      if (it == sequence_state.interned_location_names.end()) {
+        context_->storage->IncrementIndexedStats(
+            stats::heap_graph_invalid_string_id,
+            static_cast<int>(sequence_state.current_upid));
+
+      } else {
+        location_name = it->second;
+      }
+    }
+    auto id_and_row =
+        context_->storage->mutable_heap_graph_class_table()->Insert(
+            {interned_type.name, base::nullopt, location_name});
+
+    type_id_to_db[id] = id_and_row.id;
+    base::StringView normalized_type =
+        NormalizeTypeName(context_->storage->GetString(interned_type.name));
+
+    // Annoyingly, some apps have a relative path to base.apk. We take this to
+    // mean the main package, so we treat it as if the location was unknown.
+    bool is_base_apk = false;
+    if (location_name) {
+      base::StringView base_apk("base.apk");
+      is_base_apk = context_->storage->GetString(*location_name)
+                        .substr(0, base_apk.size()) == base_apk;
+    }
+
+    if (location_name && !is_base_apk) {
+      base::Optional<std::string> package_name =
+          PackageFromLocation(context_->storage->GetString(*location_name));
+      if (package_name) {
+        class_to_rows_[std::make_pair(
+                           context_->storage->InternString(
+                               base::StringView(*package_name)),
+                           context_->storage->InternString(normalized_type))]
+            .emplace_back(id_and_row.id);
+      }
+    } else {
+      // TODO(b/153552977): Remove this workaround.
+      // For profiles collected for old versions of perfetto_hprof, we do not
+      // have any location information. We store them using the nullopt
+      // location, and assume they are all part of the main APK.
+      //
+      // This is to keep ingestion of old profiles working (especially
+      // important for the UI).
+      class_to_rows_[std::make_pair(
+                         base::nullopt,
+                         context_->storage->InternString(normalized_type))]
+          .emplace_back(id_and_row.id);
+    }
+  }
+
   for (const SourceObject& obj : sequence_state.current_objects) {
-    auto it = sequence_state.interned_type_names.find(obj.type_id);
-    if (it == sequence_state.interned_type_names.end()) {
+    auto type_it = type_id_to_db.find(obj.type_id);
+    if (type_it == type_id_to_db.end()) {
       context_->storage->IncrementIndexedStats(
-          stats::heap_graph_invalid_string_id,
+          stats::heap_graph_malformed_packet,
           static_cast<int>(sequence_state.current_upid));
       continue;
     }
-    StringPool::Id type_name = it->second;
-    context_->storage->mutable_heap_graph_object_table()->Insert(
-        {sequence_state.current_upid, sequence_state.current_ts,
-         static_cast<int64_t>(obj.object_id),
-         static_cast<int64_t>(obj.self_size), /*retained_size=*/-1,
-         /*unique_retained_size=*/-1, /*reference_set_id=*/-1,
-         /*reachable=*/0, /*type_name=*/type_name,
-         /*deobfuscated_type_name=*/base::nullopt,
-         /*root_type=*/base::nullopt});
-    int64_t row = context_->storage->heap_graph_object_table().row_count() - 1;
+    tables::HeapGraphClassTable::Id db_id = type_it->second;
+    auto id_and_row =
+        context_->storage->mutable_heap_graph_object_table()->Insert(
+            {sequence_state.current_upid, sequence_state.current_ts,
+             static_cast<int64_t>(obj.object_id),
+             static_cast<int64_t>(obj.self_size), /*retained_size=*/-1,
+             /*unique_retained_size=*/-1, /*reference_set_id=*/base::nullopt,
+             /*reachable=*/0, db_id,
+             /*root_type=*/base::nullopt});
+    int64_t row = id_and_row.row;
     sequence_state.object_id_to_row.emplace(obj.object_id, row);
-    class_to_rows_[type_name].emplace_back(row);
-    sequence_state.walker.AddNode(row, obj.self_size,
-                                  static_cast<int32_t>(type_name.raw_id()));
+    int64_t type_row =
+        *context_->storage->heap_graph_class_table().id().IndexOf(db_id);
+    // Still using raw rows for the HeapGraphWalker to not tie it to the
+    // data base and make it useable independently.
+    // We will eventually want to use that client-side for summarization.
+    sequence_state.walker.AddNode(row, obj.self_size, type_row);
   }
 
   for (const SourceObject& obj : sequence_state.current_objects) {
     auto it = sequence_state.object_id_to_row.find(obj.object_id);
     if (it == sequence_state.object_id_to_row.end())
       continue;
-    int64_t owner_row = it->second;
+    uint32_t owner_row = it->second;
 
-    int64_t reference_set_id =
+    uint32_t reference_set_id =
         context_->storage->heap_graph_reference_table().row_count();
     std::set<int64_t> seen_owned;
     for (const SourceObject::Reference& ref : obj.references) {
@@ -150,25 +410,26 @@
       if (inserted)
         sequence_state.walker.AddEdge(owner_row, owned_row);
 
-      auto field_name_it =
-          sequence_state.interned_field_names.find(ref.field_name_id);
-      if (field_name_it == sequence_state.interned_field_names.end()) {
+      auto field_it = sequence_state.interned_fields.find(ref.field_name_id);
+      if (field_it == sequence_state.interned_fields.end()) {
         context_->storage->IncrementIndexedStats(
             stats::heap_graph_invalid_string_id,
             static_cast<int>(sequence_state.current_upid));
         continue;
       }
-      StringPool::Id field_name = field_name_it->second;
+      const InternedField& interned_field = field_it->second;
+      StringPool::Id field_name = interned_field.name;
       context_->storage->mutable_heap_graph_reference_table()->Insert(
-          {reference_set_id, owner_row, owned_row, field_name,
+          {reference_set_id, owner_row, owned_row, interned_field.name,
+           interned_field.type_name,
            /*deobfuscated_field_name=*/base::nullopt});
-      int64_t row =
+      uint32_t row =
           context_->storage->heap_graph_reference_table().row_count() - 1;
       field_to_rows_[field_name].emplace_back(row);
     }
     context_->storage->mutable_heap_graph_object_table()
         ->mutable_reference_set_id()
-        ->Set(static_cast<uint32_t>(owner_row), reference_set_id);
+        ->Set(owner_row, reference_set_id);
   }
 
   for (const SourceRoot& root : sequence_state.current_roots) {
@@ -222,30 +483,40 @@
     node_to_cumulative_count[node.parent_id] += node_to_cumulative_count[i];
   }
 
-  std::vector<uint32_t> node_to_row_idx(init_path.nodes.size());
+  std::vector<FlamegraphId> node_to_id(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;
+    base::Optional<FlamegraphId> parent_id;
     if (node.parent_id != 0)
-      parent_id = node_to_row_idx[node.parent_id];
-    const uint32_t depth = node.depth - 1;  // -1 because we do not have the
-                                            // artificial root in the database.
+      parent_id = node_to_id[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));
+    base::Optional<StringPool::Id> name =
+        context_->storage->heap_graph_class_table()
+            .deobfuscated_name()[static_cast<uint32_t>(node.class_name)];
+    if (!name) {
+      name = context_->storage->heap_graph_class_table()
+                 .name()[static_cast<uint32_t>(node.class_name)];
+    }
+    PERFETTO_CHECK(name);
+
+    tables::ExperimentalFlamegraphNodesTable::Row alloc_row{};
+    alloc_row.ts = current_ts;
+    alloc_row.upid = current_upid;
+    alloc_row.profile_type = profile_type;
+    alloc_row.depth = depth;
+    alloc_row.name = *name;
+    alloc_row.map_name = java_mapping;
+    alloc_row.count = static_cast<int64_t>(node.count);
+    alloc_row.cumulative_count =
+        static_cast<int64_t>(node_to_cumulative_count[i]);
+    alloc_row.size = static_cast<int64_t>(node.size);
+    alloc_row.cumulative_size =
+        static_cast<int64_t>(node_to_cumulative_size[i]);
+    alloc_row.parent_id = parent_id;
+    node_to_id[i] = tbl->Insert(alloc_row).id;
   }
   return tbl;
 }
@@ -267,5 +538,36 @@
       ->Set(static_cast<uint32_t>(row), unique_retained);
 }
 
+void HeapGraphTracker::NotifyEndOfFile() {
+  if (!sequence_state_.empty()) {
+    context_->storage->IncrementStats(stats::heap_graph_non_finalized_graph);
+  }
+}
+
+StringPool::Id HeapGraphTracker::MaybeDeobfuscate(
+    base::Optional<StringPool::Id> package_name,
+    StringPool::Id id) {
+  base::StringView type_name = context_->storage->GetString(id);
+  auto normalized_type = GetNormalizedType(type_name);
+  auto it = deobfuscation_mapping_.find(std::make_pair(
+      package_name, context_->storage->InternString(normalized_type.name)));
+  if (it == deobfuscation_mapping_.end())
+    return id;
+
+  base::StringView normalized_deobfuscated_name =
+      context_->storage->GetString(it->second);
+  std::string result =
+      DenormalizeTypeName(normalized_type, normalized_deobfuscated_name);
+  return context_->storage->InternString(base::StringView(result));
+}
+
+void HeapGraphTracker::AddDeobfuscationMapping(
+    base::Optional<StringPool::Id> package_name,
+    StringPool::Id obfuscated_name,
+    StringPool::Id deobfuscated_name) {
+  deobfuscation_mapping_.emplace(std::make_pair(package_name, obfuscated_name),
+                                 deobfuscated_name);
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/heap_graph_tracker.h b/src/trace_processor/importers/proto/heap_graph_tracker.h
index 211b924..eb25b21 100644
--- a/src/trace_processor/importers/proto/heap_graph_tracker.h
+++ b/src/trace_processor/importers/proto/heap_graph_tracker.h
@@ -21,17 +21,32 @@
 #include <vector>
 
 #include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/string_view.h"
 
 #include "protos/perfetto/trace/profiling/heap_graph.pbzero.h"
 #include "src/trace_processor/importers/proto/heap_graph_walker.h"
-#include "src/trace_processor/trace_processor_context.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 namespace perfetto {
 namespace trace_processor {
 
 class TraceProcessorContext;
 
+struct NormalizedType {
+  base::StringView name;
+  bool is_static_class;
+  size_t number_of_arrays;
+};
+
+base::Optional<std::string> PackageFromLocation(base::StringView location);
+base::Optional<base::StringView> GetStaticClassTypeName(base::StringView type);
+size_t NumberOfArrays(base::StringView type);
+NormalizedType GetNormalizedType(base::StringView type);
+base::StringView NormalizeTypeName(base::StringView type);
+std::string DenormalizeTypeName(NormalizedType normalized,
+                                base::StringView deobfuscated_type_name);
+
 class HeapGraphTracker : public HeapGraphWalker::Delegate, public Destructible {
  public:
   struct SourceObject {
@@ -66,9 +81,16 @@
   void AddInternedTypeName(uint32_t seq_id,
                            uint64_t intern_id,
                            StringPool::Id strid);
+  void AddInternedType(uint32_t seq_id,
+                       uint64_t intern_id,
+                       StringPool::Id strid,
+                       uint64_t location_id);
   void AddInternedFieldName(uint32_t seq_id,
                             uint64_t intern_id,
-                            StringPool::Id strid);
+                            base::StringView str);
+  void AddInternedLocationName(uint32_t seq_id,
+                               uint64_t intern_id,
+                               StringPool::Id str);
   void FinalizeProfile(uint32_t seq);
   void SetPacketIndex(uint32_t seq_id, uint64_t index);
 
@@ -78,9 +100,18 @@
   void SetRetained(int64_t row,
                    int64_t retained,
                    int64_t unique_retained) override;
+  void NotifyEndOfFile();
 
-  const std::vector<int64_t>* RowsForType(StringPool::Id type_name) const {
-    auto it = class_to_rows_.find(type_name);
+  void AddDeobfuscationMapping(base::Optional<StringPool::Id> package_name,
+                               StringPool::Id obfuscated_name,
+                               StringPool::Id deobfuscated_name);
+  StringPool::Id MaybeDeobfuscate(base::Optional<StringPool::Id> package_name,
+                                  StringPool::Id);
+
+  const std::vector<tables::HeapGraphClassTable::Id>* RowsForType(
+      base::Optional<StringPool::Id> package_name,
+      StringPool::Id type_name) const {
+    auto it = class_to_rows_.find(std::make_pair(package_name, type_name));
     if (it == class_to_rows_.end())
       return nullptr;
     return &it->second;
@@ -97,7 +128,18 @@
       const int64_t current_ts,
       const UniquePid current_upid);
 
+  // public for testing.
+  base::Optional<std::string> PackageFromLocation(base::StringView location);
+
  private:
+  struct InternedField {
+    StringPool::Id name;
+    StringPool::Id type_name;
+  };
+  struct InternedType {
+    StringPool::Id name;
+    base::Optional<uint64_t> location_id;
+  };
   struct SequenceState {
     SequenceState(HeapGraphTracker* tracker) : walker(tracker) {}
 
@@ -105,23 +147,29 @@
     int64_t current_ts = 0;
     std::vector<SourceObject> current_objects;
     std::vector<SourceRoot> current_roots;
-    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;
+    std::map<uint64_t, InternedType> interned_types;
+    std::map<uint64_t, StringPool::Id> interned_location_names;
+    std::map<uint64_t, InternedField> interned_fields;
+    std::map<uint64_t, uint32_t> object_id_to_row;
+    base::Optional<uint64_t> prev_index;
     HeapGraphWalker walker;
   };
 
   SequenceState& GetOrCreateSequence(uint32_t seq_id);
   bool SetPidAndTimestamp(SequenceState* seq, UniquePid upid, int64_t ts);
 
-
   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<std::pair<base::Optional<StringPool::Id>, StringPool::Id>,
+           std::vector<tables::HeapGraphClassTable::Id>>
+      class_to_rows_;
   std::map<StringPool::Id, std::vector<int64_t>> field_to_rows_;
+
+  std::map<std::pair<base::Optional<StringPool::Id>, StringPool::Id>,
+           StringPool::Id>
+      deobfuscation_mapping_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc b/src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc
index 3cb5c2a..796956c 100644
--- a/src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc
+++ b/src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc
@@ -25,6 +25,15 @@
 
 using ::testing::UnorderedElementsAre;
 
+TEST(HeapGraphTrackerTest, PackageFromLocationApp) {
+  TraceProcessorContext context;
+  HeapGraphTracker tracker(&context);
+  EXPECT_EQ(tracker.PackageFromLocation(
+                "/data/app/~~ASDFGH1234QWerT==/"
+                "com.twitter.android-MNBVCX7890SDTst6==/test.apk"),
+            "com.twitter.android");
+}
+
 TEST(HeapGraphTrackerTest, BuildFlamegraph) {
   //           4@A 5@B
   //             \ /
@@ -48,7 +57,7 @@
   constexpr uint64_t kA = 3;
   constexpr uint64_t kB = 4;
 
-  StringPool::Id field = context.storage->InternString("foo");
+  base::StringView field = base::StringView("foo");
   StringPool::Id x = context.storage->InternString("X");
   StringPool::Id y = context.storage->InternString("Y");
   StringPool::Id a = context.storage->InternString("A");
@@ -142,6 +151,52 @@
   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";
+static const char kStaticClassNoArray[] = "java.lang.Class<abc>";
+static const char kStaticClassArray[] = "java.lang.Class<abc[]>";
+
+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");
+  EXPECT_EQ(NormalizeTypeName(base::StringView(kStaticClassNoArray,
+                                               sizeof(kStaticClassNoArray) - 1))
+                .ToStdString(),
+            "abc");
+  EXPECT_EQ(NormalizeTypeName(base::StringView(kStaticClassArray,
+                                               sizeof(kStaticClassArray) - 1))
+                .ToStdString(),
+            "abc");
+}
+
+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 c7ba842..0082a3f 100644
--- a/src/trace_processor/importers/proto/heap_graph_walker.cc
+++ b/src/trace_processor/importers/proto/heap_graph_walker.cc
@@ -59,7 +59,9 @@
 
 HeapGraphWalker::Delegate::~Delegate() = default;
 
-void HeapGraphWalker::AddNode(int64_t row, uint64_t size, int32_t class_name) {
+void HeapGraphWalker::AddNode(int64_t row,
+                              uint64_t size,
+                              ClassNameId class_name) {
   if (static_cast<size_t>(row) >= nodes_.size())
     nodes_.resize(static_cast<size_t>(row) + 1);
   Node& node = GetNode(row);
diff --git a/src/trace_processor/importers/proto/heap_graph_walker.h b/src/trace_processor/importers/proto/heap_graph_walker.h
index 052320b..128aa57 100644
--- a/src/trace_processor/importers/proto/heap_graph_walker.h
+++ b/src/trace_processor/importers/proto/heap_graph_walker.h
@@ -97,7 +97,7 @@
 
 class HeapGraphWalker {
  public:
-  using ClassNameId = int32_t;
+  using ClassNameId = int64_t;
 
   struct PathFromRoot {
     static constexpr size_t kRoot = 0;
@@ -107,7 +107,7 @@
       size_t parent_id = 0;
       uint64_t size = 0;
       uint64_t count = 0;
-      ClassNameId class_name = -1;
+      ClassNameId class_name = 0;
       std::map<ClassNameId, size_t> children;
     };
     std::vector<Node> nodes{Node{}};
@@ -125,7 +125,7 @@
   HeapGraphWalker(Delegate* delegate) : delegate_(delegate) {}
 
   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) { AddNode(row, size, 0); }
   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
@@ -149,7 +149,7 @@
     uint64_t lowlink = 0;
     int64_t component = -1;
 
-    int32_t class_name = -1;
+    ClassNameId class_name = 0;
     int32_t distance_to_root = -1;
 
     bool on_stack = false;
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 9326133..3a55dda 100644
--- a/src/trace_processor/importers/proto/heap_graph_walker_unittest.cc
+++ b/src/trace_processor/importers/proto/heap_graph_walker_unittest.cc
@@ -596,7 +596,7 @@
 }
 
 bool HasPath(const HeapGraphWalker::PathFromRoot& path,
-             std::vector<int32_t> class_names) {
+             std::vector<HeapGraphWalker::ClassNameId> class_names) {
   return HasPath(path, path.nodes[HeapGraphWalker::PathFromRoot::kRoot],
                  std::move(class_names));
 }
@@ -611,9 +611,9 @@
 TEST(HeapGraphWalkerTest, ShortestPath) {
   HeapGraphWalkerTestDelegate delegate;
   HeapGraphWalker walker(&delegate);
-  walker.AddNode(1, 1, 1);
-  walker.AddNode(2, 2, 2);
-  walker.AddNode(3, 3, 3);
+  walker.AddNode(1, 1, 1u);
+  walker.AddNode(2, 2, 2u);
+  walker.AddNode(3, 3, 3u);
   walker.AddNode(4, 4, 4);
 
   walker.AddEdge(2, 1);
@@ -625,9 +625,9 @@
   walker.MarkRoot(4);
   auto path = walker.FindPathsFromRoot();
 
-  EXPECT_TRUE(HasPath(path, {4, 2, 1}));
-  EXPECT_TRUE(HasPath(path, {4, 2, 3}));
-  EXPECT_FALSE(HasPath(path, {4, 2, 3, 1}));
+  EXPECT_TRUE(HasPath(path, {4u, 2u, 1u}));
+  EXPECT_TRUE(HasPath(path, {4u, 2u, 3u}));
+  EXPECT_FALSE(HasPath(path, {4u, 2u, 3u, 1u}));
 }
 
 //    1      |
@@ -640,10 +640,10 @@
 TEST(HeapGraphWalkerTest, ShortestPathMultipleRoots) {
   HeapGraphWalkerTestDelegate delegate;
   HeapGraphWalker walker(&delegate);
-  walker.AddNode(1, 1, 1);
-  walker.AddNode(2, 2, 2);
-  walker.AddNode(3, 3, 3);
-  walker.AddNode(4, 4, 4);
+  walker.AddNode(1, 1, 1u);
+  walker.AddNode(2, 2, 2u);
+  walker.AddNode(3, 3, 3u);
+  walker.AddNode(4, 4, 4u);
 
   walker.AddEdge(2, 1);
   walker.AddEdge(2, 3);
@@ -655,9 +655,9 @@
   walker.MarkRoot(2);
   auto path = walker.FindPathsFromRoot();
 
-  EXPECT_TRUE(HasPath(path, {2, 1}));
-  EXPECT_TRUE(HasPath(path, {2, 3}));
-  EXPECT_FALSE(HasPath(path, {4, 2, 3}));
+  EXPECT_TRUE(HasPath(path, {2u, 1u}));
+  EXPECT_TRUE(HasPath(path, {2u, 3u}));
+  EXPECT_FALSE(HasPath(path, {4u, 2u, 3u}));
 }
 
 }  // namespace
diff --git a/src/trace_processor/heap_profile_tracker.cc b/src/trace_processor/importers/proto/heap_profile_tracker.cc
similarity index 75%
rename from src/trace_processor/heap_profile_tracker.cc
rename to src/trace_processor/importers/proto/heap_profile_tracker.cc
index a2aff3a..14d99be 100644
--- a/src/trace_processor/heap_profile_tracker.cc
+++ b/src/trace_processor/importers/proto/heap_profile_tracker.cc
@@ -14,12 +14,14 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/heap_profile_tracker.h"
-
-#include "src/trace_processor/process_tracker.h"
-#include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/importers/proto/heap_profile_tracker.h"
 
 #include "perfetto/base/logging.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
+#include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -45,13 +47,11 @@
   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])));
+  uint32_t frame_idx =
+      *frames_tbl.id().IndexOf(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])));
+  uint32_t mapping_idx =
+      *mapping_tbl.id().IndexOf(frames_tbl.mapping()[frame_idx]);
   StringId mapping_name = mapping_tbl.name()[mapping_idx];
 
   base::Optional<uint32_t> symbol_set_id =
@@ -98,13 +98,14 @@
           storage->mutable_string_pool(), nullptr));
 
   // FORWARD PASS:
-  // Aggregate callstacks by frame name / mapping name. Use symbolization data.
+  // 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));
+
+    auto opt_parent_id = callsites_tbl.parent_id()[i];
+    if (opt_parent_id) {
+      parent_idx = callsites_tbl.id().IndexOf(*opt_parent_id);
       parent_idx = callsite_to_merged_callsite[*parent_idx];
       PERFETTO_CHECK(*parent_idx < i);
     }
@@ -128,9 +129,9 @@
         row.name = merged_callsite.frame_name;
         row.map_name = merged_callsite.mapping_name;
         if (parent_idx)
-          row.parent_id = tbl->id()[*parent_idx].value;
+          row.parent_id = tbl->id()[*parent_idx];
 
-        parent_idx = *tbl->id().IndexOf(tbl->Insert(std::move(row)));
+        parent_idx = tbl->Insert(std::move(row)).row;
         PERFETTO_CHECK(merged_callsites_to_table_idx.size() ==
                        tbl->row_count());
       }
@@ -143,7 +144,7 @@
   // 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)});
+      {allocation_tbl.ts().le(timestamp), allocation_tbl.upid().eq(upid)});
 
   if (filtered.row_count() == 0) {
     return nullptr;
@@ -164,19 +165,17 @@
                 tables::HeapProfileAllocationTable::ColumnIndex::callsite_id))
             .long_value;
 
-    PERFETTO_CHECK((size < 0 && count < 0) || (size >= 0 && count >= 0));
+    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 (size > 0) {
-      // TODO(fmayer): Clean up types and remove the static_cast.
+    if (count > 0) {
       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);
   }
@@ -227,11 +226,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(
@@ -240,29 +255,27 @@
     const SourceAllocation& alloc,
     const StackProfileTracker::InternLookup* intern_lookup) {
   SequenceState& sequence_state = sequence_state_[seq_id];
-  auto maybe_callstack_id = stack_profile_tracker->FindOrInsertCallstack(
+
+  auto opt_callstack_id = stack_profile_tracker->FindOrInsertCallstack(
       alloc.callstack_id, intern_lookup);
-  if (!maybe_callstack_id)
+  if (!opt_callstack_id)
     return;
 
-  CallsiteId callstack_id = *maybe_callstack_id;
+  CallsiteId callstack_id = *opt_callstack_id;
 
   UniquePid upid = context_->process_tracker->GetOrCreateProcess(
       static_cast<uint32_t>(alloc.pid));
 
   tables::HeapProfileAllocationTable::Row alloc_row{
-      alloc.timestamp, upid, callstack_id.value,
+      alloc.timestamp, upid, callstack_id,
       static_cast<int64_t>(alloc.alloc_count),
       static_cast<int64_t>(alloc.self_allocated)};
 
   tables::HeapProfileAllocationTable::Row free_row{
-      alloc.timestamp, upid, callstack_id.value,
+      alloc.timestamp, upid, callstack_id,
       -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(
@@ -271,8 +284,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()) {
@@ -282,15 +293,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;
@@ -320,5 +375,14 @@
   stack_profile_tracker->ClearIndices();
 }
 
+void HeapProfileTracker::NotifyEndOfFile() {
+  for (const auto& key_and_sequence_state : sequence_state_) {
+    const SequenceState& sequence_state = key_and_sequence_state.second;
+    if (!sequence_state.pending_allocs.empty()) {
+      context_->storage->IncrementStats(stats::heapprofd_non_finalized_profile);
+    }
+  }
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/heap_profile_tracker.h b/src/trace_processor/importers/proto/heap_profile_tracker.h
similarity index 63%
rename from src/trace_processor/heap_profile_tracker.h
rename to src/trace_processor/importers/proto/heap_profile_tracker.h
index 53e7637..91cfe72 100644
--- a/src/trace_processor/heap_profile_tracker.h
+++ b/src/trace_processor/importers/proto/heap_profile_tracker.h
@@ -14,26 +14,21 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_HEAP_PROFILE_TRACKER_H_
-#define SRC_TRACE_PROCESSOR_HEAP_PROFILE_TRACKER_H_
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_HEAP_PROFILE_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_HEAP_PROFILE_TRACKER_H_
 
-#include <deque>
+#include <set>
 #include <unordered_map>
 
 #include "perfetto/ext/base/optional.h"
-
-#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
-#include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
-#include "src/trace_processor/stack_profile_tracker.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
+#include "src/trace_processor/storage/trace_storage.h"
 
 namespace perfetto {
 namespace trace_processor {
 
-std::unique_ptr<tables::ExperimentalFlamegraphNodesTable> BuildNativeFlamegraph(
-    TraceStorage* storage,
-    UniquePid upid,
-    int64_t timestamp);
+std::unique_ptr<tables::ExperimentalFlamegraphNodesTable>
+BuildNativeFlamegraph(TraceStorage* storage, UniquePid upid, int64_t timestamp);
 
 class TraceProcessorContext;
 
@@ -70,6 +65,8 @@
                          StackProfileTracker* stack_profile_tracker,
                          const StackProfileTracker::InternLookup* lookup);
 
+  void NotifyEndOfFile();
+
   ~HeapProfileTracker();
 
  private:
@@ -89,7 +86,27 @@
                        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_;
@@ -99,4 +116,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_HEAP_PROFILE_TRACKER_H_
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_HEAP_PROFILE_TRACKER_H_
diff --git a/src/trace_processor/heap_profile_tracker_unittest.cc b/src/trace_processor/importers/proto/heap_profile_tracker_unittest.cc
similarity index 92%
rename from src/trace_processor/heap_profile_tracker_unittest.cc
rename to src/trace_processor/importers/proto/heap_profile_tracker_unittest.cc
index d9d3e1d..1a4e493 100644
--- a/src/trace_processor/heap_profile_tracker_unittest.cc
+++ b/src/trace_processor/importers/proto/heap_profile_tracker_unittest.cc
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/heap_profile_tracker.h"
+#include "src/trace_processor/importers/proto/heap_profile_tracker.h"
 
-#include "src/trace_processor/stack_profile_tracker.h"
-#include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 #include "test/gtest_and_gmock.h"
 
 namespace perfetto {
@@ -149,7 +149,7 @@
 
   const auto& frames = context.storage->stack_profile_frame_table();
   EXPECT_THAT(frames.name()[0], frame_name);
-  EXPECT_THAT(frames.mapping()[0], 0);
+  EXPECT_THAT(frames.mapping()[0], MappingId{0});
   EXPECT_THAT(frames.rel_pc()[0], kFrameRelPc);
 }
 
@@ -168,28 +168,28 @@
   const auto& parent_id = callsite_table.parent_id();
   const auto& frame_id = callsite_table.frame_id();
 
-  EXPECT_EQ(depth[0], 0);
-  EXPECT_EQ(depth[1], 1);
+  EXPECT_EQ(depth[0], 0u);
+  EXPECT_EQ(depth[1], 1u);
 
-  EXPECT_EQ(parent_id[0], -1);
-  EXPECT_EQ(parent_id[1], 0);
+  EXPECT_EQ(parent_id[0], base::nullopt);
+  EXPECT_EQ(parent_id[1], CallsiteId{0});
 
-  EXPECT_EQ(frame_id[0], 0);
-  EXPECT_EQ(frame_id[1], 0);
+  EXPECT_EQ(frame_id[0], FrameId{0});
+  EXPECT_EQ(frame_id[1], FrameId{0});
 }
 
-int64_t FindCallstack(const TraceStorage& storage,
-                      int64_t depth,
-                      int64_t parent,
-                      FrameId frame_id) {
+base::Optional<CallsiteId> FindCallstack(const TraceStorage& storage,
+                                         int64_t depth,
+                                         base::Optional<CallsiteId> parent,
+                                         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.value) {
-      return static_cast<int64_t>(i);
+        callsites.frame_id()[i] == frame_id) {
+      return callsites.id()[i];
     }
   }
-  return -1;
+  return base::nullopt;
 }
 
 TEST(HeapProfileTrackerTest, SourceMappingPath) {
@@ -323,13 +323,13 @@
   hpt->CommitAllocations(kDefaultSequence, spt.get(), nullptr);
 
   for (size_t i = 0; i < base::ArraySize(callstacks); ++i) {
-    int64_t parent = -1;
+    base::Optional<CallsiteId> parent;
     const StackProfileTracker::SourceCallstack& callstack = callstacks[i];
     for (size_t depth = 0; depth < callstack.size(); ++depth) {
       auto frame_id = spt->GetDatabaseFrameIdForTesting(callstack[depth]);
-      int64_t self = FindCallstack(
+      base::Optional<CallsiteId> self = FindCallstack(
           *context.storage, static_cast<int64_t>(depth), parent, frame_id);
-      ASSERT_NE(self, -1);
+      ASSERT_TRUE(self.has_value());
       parent = self;
     }
   }
diff --git a/src/trace_processor/metadata_tracker.cc b/src/trace_processor/importers/proto/metadata_tracker.cc
similarity index 87%
rename from src/trace_processor/metadata_tracker.cc
rename to src/trace_processor/importers/proto/metadata_tracker.cc
index e105d01..757ac1a 100644
--- a/src/trace_processor/metadata_tracker.cc
+++ b/src/trace_processor/importers/proto/metadata_tracker.cc
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/metadata_tracker.h"
+#include "src/trace_processor/importers/proto/metadata_tracker.h"
 
-#include "src/trace_processor/process_tracker.h"
-#include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -50,9 +50,9 @@
   row.name = key_ids_[key_idx];
   row.key_type = key_type_ids_[static_cast<size_t>(metadata::KeyType::kSingle)];
 
-  MetadataId id = metadata_table->Insert(row);
-  WriteValue(*metadata_table->id().IndexOf(id), value);
-  return id;
+  auto id_and_row = metadata_table->Insert(row);
+  WriteValue(id_and_row.row, value);
+  return id_and_row.id;
 }
 
 MetadataId MetadataTracker::AppendMetadata(metadata::KeyIDs key,
@@ -67,9 +67,9 @@
   row.key_type = key_type_ids_[static_cast<size_t>(metadata::KeyType::kMulti)];
 
   auto* metadata_table = context_->storage->mutable_metadata_table();
-  MetadataId id = metadata_table->Insert(row);
-  WriteValue(*metadata_table->id().IndexOf(id), value);
-  return id;
+  auto id_and_row = metadata_table->Insert(row);
+  WriteValue(id_and_row.row, value);
+  return id_and_row.id;
 }
 
 void MetadataTracker::WriteValue(uint32_t row, Variadic value) {
diff --git a/src/trace_processor/metadata_tracker.h b/src/trace_processor/importers/proto/metadata_tracker.h
similarity index 87%
rename from src/trace_processor/metadata_tracker.h
rename to src/trace_processor/importers/proto/metadata_tracker.h
index 38685ac..b2ecda3 100644
--- a/src/trace_processor/metadata_tracker.h
+++ b/src/trace_processor/importers/proto/metadata_tracker.h
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_METADATA_TRACKER_H_
-#define SRC_TRACE_PROCESSOR_METADATA_TRACKER_H_
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_METADATA_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_METADATA_TRACKER_H_
 
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/trace_storage.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -58,4 +58,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_METADATA_TRACKER_H_
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_METADATA_TRACKER_H_
diff --git a/src/trace_processor/importers/proto/packet_sequence_state.h b/src/trace_processor/importers/proto/packet_sequence_state.h
index 81ec6e0..1bd9cae 100644
--- a/src/trace_processor/importers/proto/packet_sequence_state.h
+++ b/src/trace_processor/importers/proto/packet_sequence_state.h
@@ -24,12 +24,13 @@
 
 #include "perfetto/base/compiler.h"
 #include "perfetto/protozero/proto_decoder.h"
-#include "src/trace_processor/stack_profile_tracker.h"
+#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
+#include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/trace_blob_view.h"
-#include "src/trace_processor/trace_processor_context.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 #include "protos/perfetto/trace/trace_packet_defaults.pbzero.h"
+#include "protos/perfetto/trace/track_event/track_event.pbzero.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -156,22 +157,38 @@
   template <uint32_t FieldId, typename MessageType>
   typename MessageType::Decoder* LookupInternedMessage(uint64_t iid);
 
-  // Returns |nullptr| if no defaults were set in the given generation.
+  // Returns |nullptr| if no defaults were set.
   InternedMessageView* GetTracePacketDefaultsView() {
     if (!trace_packet_defaults_)
       return nullptr;
     return &trace_packet_defaults_.value();
   }
 
-  // Returns |nullptr| if no defaults were set in the given generation.
-  typename protos::pbzero::TracePacketDefaults::Decoder*
-  GetTracePacketDefaults() {
+  // Returns |nullptr| if no defaults were set.
+  protos::pbzero::TracePacketDefaults::Decoder* GetTracePacketDefaults() {
     InternedMessageView* view = GetTracePacketDefaultsView();
     if (!view)
       return nullptr;
     return view->GetOrCreateDecoder<protos::pbzero::TracePacketDefaults>();
   }
 
+  // Returns |nullptr| if no TrackEventDefaults were set.
+  protos::pbzero::TrackEventDefaults::Decoder* GetTrackEventDefaults() {
+    auto* packet_defaults_view = GetTracePacketDefaultsView();
+    if (packet_defaults_view) {
+      auto* track_event_defaults_view =
+          packet_defaults_view
+              ->GetOrCreateSubmessageView<protos::pbzero::TracePacketDefaults,
+                                          protos::pbzero::TracePacketDefaults::
+                                              kTrackEventDefaultsFieldNumber>();
+      if (track_event_defaults_view) {
+        return track_event_defaults_view
+            ->GetOrCreateDecoder<protos::pbzero::TrackEventDefaults>();
+      }
+    }
+    return nullptr;
+  }
+
   PacketSequenceState* state() const { return state_; }
 
  private:
diff --git a/src/trace_processor/importers/proto/perf_sample_tracker.cc b/src/trace_processor/importers/proto/perf_sample_tracker.cc
new file mode 100644
index 0000000..06b5ffa
--- /dev/null
+++ b/src/trace_processor/importers/proto/perf_sample_tracker.cc
@@ -0,0 +1,108 @@
+/*
+ * 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/perf_sample_tracker.h"
+
+#include <vector>
+
+#include <inttypes.h>
+
+#include "perfetto/ext/base/optional.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <cxxabi.h>
+#endif
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+// TODO(rsavitski): consider using the sampling rate from the trace config.
+constexpr int64_t kFixedStackSliceDurationNs = 1 * 1000 * 1000;
+}  // namespace
+
+void PerfSampleTracker::AddStackToSliceTrack(int64_t timestamp,
+                                             CallsiteId leaf_id,
+                                             uint32_t pid,
+                                             uint32_t tid,
+                                             uint32_t cpu) {
+  UniquePid upid =
+      context_->process_tracker->GetOrCreateProcess(static_cast<uint32_t>(pid));
+
+  TrackId track_id = context_->track_tracker->InternPerfStackTrack(upid);
+  const auto& callsites = context_->storage->stack_profile_callsite_table();
+  const auto& frames = context_->storage->stack_profile_frame_table();
+  const auto& mappings = context_->storage->stack_profile_mapping_table();
+
+  // Synthetic frame for more context, as the track is process-wide.
+  char buf[128] = {};
+  snprintf(buf, sizeof(buf), "cpu: [%" PRIu32 "]; thread: [%" PRIi32 "]", cpu,
+           tid);
+  StringId synth = context_->storage->InternString(buf);
+  context_->slice_tracker->Scoped(timestamp, track_id, kNullStringId, synth,
+                                  kFixedStackSliceDurationNs);
+
+  // The callstack id references the leaf frame, while we want the slice stack
+  // to have the root frame at the top in the UI, so walk the chain in reverse.
+  std::vector<uint32_t> callsite_rows;
+  callsite_rows.reserve(64);
+  base::Optional<CallsiteId> cs_id = leaf_id;
+  while (cs_id) {
+    uint32_t row = *callsites.id().IndexOf(*cs_id);
+    callsite_rows.push_back(row);
+    cs_id = callsites.parent_id()[row];
+  }
+
+  for (auto rit = callsite_rows.rbegin(); rit != callsite_rows.rend(); ++rit) {
+    uint32_t callsite_row = *rit;
+    FrameId frame_id = callsites.frame_id()[callsite_row];
+    uint32_t frame_row = *frames.id().IndexOf(frame_id);
+
+    MappingId mapping_id = frames.mapping()[frame_row];
+    uint32_t mapping_row = *mappings.id().IndexOf(mapping_id);
+
+    StringId mangled_fname = frames.name()[frame_row];
+    StringId mname = mappings.name()[mapping_row];
+
+    StringId fname = MaybeDemangle(mangled_fname);
+    context_->slice_tracker->Scoped(timestamp, track_id, mname, fname,
+                                    kFixedStackSliceDurationNs);
+  }
+}
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+StringId PerfSampleTracker::MaybeDemangle(StringId fname) {
+  StringId ret = fname;
+  const char* raw_fname = context_->storage->GetString(fname).c_str();
+  int ignored;
+  char* data = abi::__cxa_demangle(raw_fname, nullptr, nullptr, &ignored);
+  if (data) {
+    ret = context_->storage->InternString(data);
+    free(data);
+  }
+  return ret;
+}
+#else
+StringId PerfSampleTracker::MaybeDemangle(StringId fname) {
+  return fname;
+}
+#endif
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/perf_sample_tracker.h b/src/trace_processor/importers/proto/perf_sample_tracker.h
new file mode 100644
index 0000000..81dd73e
--- /dev/null
+++ b/src/trace_processor/importers/proto/perf_sample_tracker.h
@@ -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.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PERF_SAMPLE_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PERF_SAMPLE_TRACKER_H_
+
+#include <stdint.h>
+
+#include "src/trace_processor/storage/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+class PerfSampleTracker {
+ public:
+  explicit PerfSampleTracker(TraceProcessorContext* context)
+      : context_(context) {}
+
+  // Interim UI track for visualizing stack samples as stacks of slices.
+  void AddStackToSliceTrack(int64_t timestamp,
+                            CallsiteId leaf_id,
+                            uint32_t pid,
+                            uint32_t tid,
+                            uint32_t cpu);
+
+ private:
+  StringId MaybeDemangle(StringId original);
+
+  TraceProcessorContext* const context_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PERF_SAMPLE_TRACKER_H_
diff --git a/src/trace_processor/importers/proto/profile_module.cc b/src/trace_processor/importers/proto/profile_module.cc
new file mode 100644
index 0000000..0355524
--- /dev/null
+++ b/src/trace_processor/importers/proto/profile_module.cc
@@ -0,0 +1,152 @@
+/*
+ * 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/profile_module.h"
+
+#include "perfetto/base/logging.h"
+#include "src/trace_processor/importers/common/clock_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/importers/proto/profile_packet_utils.h"
+#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/tables/profiler_tables.h"
+#include "src/trace_processor/timestamped_trace_piece.h"
+#include "src/trace_processor/trace_sorter.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+#include "protos/perfetto/trace/clock_snapshot.pbzero.h"
+#include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+using perfetto::protos::pbzero::TracePacket;
+using protozero::ConstBytes;
+
+ProfileModule::ProfileModule(TraceProcessorContext* context)
+    : context_(context) {
+  RegisterForField(TracePacket::kStreamingProfilePacketFieldNumber, context);
+}
+
+ProfileModule::~ProfileModule() = default;
+
+ModuleResult ProfileModule::TokenizePacket(const TracePacket::Decoder& decoder,
+                                           TraceBlobView* packet,
+                                           int64_t /*packet_timestamp*/,
+                                           PacketSequenceState* state,
+                                           uint32_t field_id) {
+  switch (field_id) {
+    case TracePacket::kStreamingProfilePacketFieldNumber:
+      return TokenizeStreamingProfilePacket(state, packet,
+                                            decoder.streaming_profile_packet());
+  }
+  return ModuleResult::Ignored();
+}
+
+void ProfileModule::ParsePacket(const TracePacket::Decoder& decoder,
+                                const TimestampedTracePiece& ttp,
+                                uint32_t field_id) {
+  switch (field_id) {
+    case TracePacket::kStreamingProfilePacketFieldNumber:
+      PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kTracePacket);
+      ParseStreamingProfilePacket(ttp.timestamp, ttp.packet_data.sequence_state,
+                                  decoder.streaming_profile_packet());
+      return;
+  }
+}
+
+ModuleResult ProfileModule::TokenizeStreamingProfilePacket(
+    PacketSequenceState* sequence_state,
+    TraceBlobView* packet,
+    ConstBytes streaming_profile_packet) {
+  protos::pbzero::StreamingProfilePacket::Decoder decoder(
+      streaming_profile_packet.data, streaming_profile_packet.size);
+
+  // We have to resolve the reference timestamp of a StreamingProfilePacket
+  // during tokenization. If we did this during parsing instead, the
+  // tokenization of a subsequent ThreadDescriptor with a new reference
+  // timestamp would cause us to later calculate timestamps based on the wrong
+  // reference value during parsing. Since StreamingProfilePackets only need to
+  // be sorted correctly with respect to process/thread metadata events (so that
+  // pid/tid are resolved correctly during parsing), we forward the packet as a
+  // whole through the sorter, using the "root" timestamp of the packet, i.e.
+  // the current timestamp of the packet sequence.
+  auto packet_ts =
+      sequence_state->IncrementAndGetTrackEventTimeNs(/*delta_ns=*/0);
+  auto trace_ts = context_->clock_tracker->ToTraceTime(
+      protos::pbzero::ClockSnapshot::Clock::MONOTONIC, packet_ts);
+  if (trace_ts)
+    packet_ts = *trace_ts;
+
+  // Increment the sequence's timestamp by all deltas.
+  for (auto timestamp_it = decoder.timestamp_delta_us(); timestamp_it;
+       ++timestamp_it) {
+    sequence_state->IncrementAndGetTrackEventTimeNs(*timestamp_it * 1000);
+  }
+
+  context_->sorter->PushTracePacket(packet_ts, sequence_state,
+                                    std::move(*packet));
+  return ModuleResult::Handled();
+}
+
+void ProfileModule::ParseStreamingProfilePacket(
+    int64_t timestamp,
+    PacketSequenceStateGeneration* sequence_state,
+    ConstBytes streaming_profile_packet) {
+  protos::pbzero::StreamingProfilePacket::Decoder packet(
+      streaming_profile_packet.data, streaming_profile_packet.size);
+
+  ProcessTracker* procs = context_->process_tracker.get();
+  TraceStorage* storage = context_->storage.get();
+  StackProfileTracker& stack_profile_tracker =
+      sequence_state->state()->stack_profile_tracker();
+  ProfilePacketInternLookup intern_lookup(sequence_state);
+
+  uint32_t pid = static_cast<uint32_t>(sequence_state->state()->pid());
+  uint32_t tid = static_cast<uint32_t>(sequence_state->state()->tid());
+  UniqueTid utid = procs->UpdateThread(tid, pid);
+
+  // Iterate through timestamps and callstacks simultaneously.
+  auto timestamp_it = packet.timestamp_delta_us();
+  for (auto callstack_it = packet.callstack_iid(); callstack_it;
+       ++callstack_it, ++timestamp_it) {
+    if (!timestamp_it) {
+      context_->storage->IncrementStats(stats::stackprofile_parser_error);
+      PERFETTO_ELOG(
+          "StreamingProfilePacket has less callstack IDs than timestamps!");
+      break;
+    }
+
+    auto opt_cs_id = stack_profile_tracker.FindOrInsertCallstack(
+        *callstack_it, &intern_lookup);
+    if (!opt_cs_id) {
+      context_->storage->IncrementStats(stats::stackprofile_parser_error);
+      PERFETTO_ELOG("StreamingProfilePacket referencing invalid callstack!");
+      continue;
+    }
+
+    // Resolve the delta timestamps based on the packet's root timestamp.
+    timestamp += *timestamp_it * 1000;
+
+    tables::CpuProfileStackSampleTable::Row sample_row{
+        timestamp, *opt_cs_id, utid, packet.process_priority()};
+    storage->mutable_cpu_profile_stack_sample_table()->Insert(sample_row);
+  }
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/profile_module.h b/src/trace_processor/importers/proto/profile_module.h
new file mode 100644
index 0000000..fe30e03
--- /dev/null
+++ b/src/trace_processor/importers/proto/profile_module.h
@@ -0,0 +1,64 @@
+/*
+ * 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_IMPORTERS_PROTO_PROFILE_MODULE_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROFILE_MODULE_H_
+
+#include "perfetto/protozero/field.h"
+#include "src/trace_processor/importers/proto/proto_importer_module.h"
+#include "src/trace_processor/importers/proto/proto_incremental_state.h"
+
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Importer module for heap and CPU sampling profile data.
+// TODO(eseckler): Currently handles only StreamingProfilePackets. Also move
+// other profiling data import functionality into this module.
+class ProfileModule : public ProtoImporterModule {
+ public:
+  explicit ProfileModule(TraceProcessorContext* context);
+  ~ProfileModule() override;
+
+  ModuleResult TokenizePacket(
+      const protos::pbzero::TracePacket::Decoder& decoder,
+      TraceBlobView* packet,
+      int64_t packet_timestamp,
+      PacketSequenceState* state,
+      uint32_t field_id) override;
+
+  void ParsePacket(const protos::pbzero::TracePacket::Decoder& decoder,
+                   const TimestampedTracePiece& ttp,
+                   uint32_t field_id) override;
+
+ private:
+  ModuleResult TokenizeStreamingProfilePacket(
+      PacketSequenceState*,
+      TraceBlobView* packet,
+      protozero::ConstBytes streaming_profile_packet);
+  void ParseStreamingProfilePacket(
+      int64_t timestamp,
+      PacketSequenceStateGeneration*,
+      protozero::ConstBytes streaming_profile_packet);
+
+  TraceProcessorContext* context_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROFILE_MODULE_H_
diff --git a/src/trace_processor/destructible.cc b/src/trace_processor/importers/proto/profile_packet_utils.cc
similarity index 77%
copy from src/trace_processor/destructible.cc
copy to src/trace_processor/importers/proto/profile_packet_utils.cc
index 22bcf6a..9478db9 100644
--- a/src/trace_processor/destructible.cc
+++ b/src/trace_processor/importers/proto/profile_packet_utils.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.
@@ -13,12 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include "src/trace_processor/destructible.h"
+
+#include "src/trace_processor/importers/proto/profile_packet_utils.h"
 
 namespace perfetto {
 namespace trace_processor {
 
-Destructible::~Destructible() = default;
+ProfilePacketInternLookup::~ProfilePacketInternLookup() = default;
 
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/profile_packet_utils.h b/src/trace_processor/importers/proto/profile_packet_utils.h
new file mode 100644
index 0000000..8b32126
--- /dev/null
+++ b/src/trace_processor/importers/proto/profile_packet_utils.h
@@ -0,0 +1,137 @@
+/*
+ * 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_IMPORTERS_PROTO_PROFILE_PACKET_UTILS_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROFILE_PACKET_UTILS_H_
+
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/string_view.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
+
+#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
+#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class ProfilePacketUtils {
+ public:
+  static StackProfileTracker::SourceMapping MakeSourceMapping(
+      const protos::pbzero::Mapping::Decoder& entry) {
+    StackProfileTracker::SourceMapping src_mapping{};
+    src_mapping.build_id = entry.build_id();
+    src_mapping.exact_offset = entry.exact_offset();
+    src_mapping.start_offset = entry.start_offset();
+    src_mapping.start = entry.start();
+    src_mapping.end = entry.end();
+    src_mapping.load_bias = entry.load_bias();
+    for (auto path_string_id_it = entry.path_string_ids(); path_string_id_it;
+         ++path_string_id_it) {
+      src_mapping.name_ids.emplace_back(*path_string_id_it);
+    }
+    return src_mapping;
+  }
+
+  static StackProfileTracker::SourceFrame MakeSourceFrame(
+      const protos::pbzero::Frame::Decoder& entry) {
+    StackProfileTracker::SourceFrame src_frame;
+    src_frame.name_id = entry.function_name_id();
+    src_frame.mapping_id = entry.mapping_id();
+    src_frame.rel_pc = entry.rel_pc();
+    return src_frame;
+  }
+
+  static StackProfileTracker::SourceCallstack MakeSourceCallstack(
+      const protos::pbzero::Callstack::Decoder& entry) {
+    StackProfileTracker::SourceCallstack src_callstack;
+    for (auto frame_it = entry.frame_ids(); frame_it; ++frame_it)
+      src_callstack.emplace_back(*frame_it);
+    return src_callstack;
+  }
+};
+
+class ProfilePacketInternLookup : public StackProfileTracker::InternLookup {
+ public:
+  explicit ProfilePacketInternLookup(PacketSequenceStateGeneration* seq_state)
+      : seq_state_(seq_state) {}
+  ~ProfilePacketInternLookup() override;
+
+  base::Optional<base::StringView> GetString(
+      StackProfileTracker::SourceStringId iid,
+      StackProfileTracker::InternedStringType type) const override {
+    protos::pbzero::InternedString::Decoder* decoder = nullptr;
+    switch (type) {
+      case StackProfileTracker::InternedStringType::kBuildId:
+        decoder = seq_state_->LookupInternedMessage<
+            protos::pbzero::InternedData::kBuildIdsFieldNumber,
+            protos::pbzero::InternedString>(iid);
+        break;
+      case StackProfileTracker::InternedStringType::kFunctionName:
+        decoder = seq_state_->LookupInternedMessage<
+            protos::pbzero::InternedData::kFunctionNamesFieldNumber,
+            protos::pbzero::InternedString>(iid);
+        break;
+      case StackProfileTracker::InternedStringType::kMappingPath:
+        decoder = seq_state_->LookupInternedMessage<
+            protos::pbzero::InternedData::kMappingPathsFieldNumber,
+            protos::pbzero::InternedString>(iid);
+        break;
+    }
+    if (!decoder)
+      return base::nullopt;
+    return base::StringView(reinterpret_cast<const char*>(decoder->str().data),
+                            decoder->str().size);
+  }
+
+  base::Optional<StackProfileTracker::SourceMapping> GetMapping(
+      StackProfileTracker::SourceMappingId iid) const override {
+    auto* decoder = seq_state_->LookupInternedMessage<
+        protos::pbzero::InternedData::kMappingsFieldNumber,
+        protos::pbzero::Mapping>(iid);
+    if (!decoder)
+      return base::nullopt;
+    return ProfilePacketUtils::MakeSourceMapping(*decoder);
+  }
+
+  base::Optional<StackProfileTracker::SourceFrame> GetFrame(
+      StackProfileTracker::SourceFrameId iid) const override {
+    auto* decoder = seq_state_->LookupInternedMessage<
+        protos::pbzero::InternedData::kFramesFieldNumber,
+        protos::pbzero::Frame>(iid);
+    if (!decoder)
+      return base::nullopt;
+    return ProfilePacketUtils::MakeSourceFrame(*decoder);
+  }
+
+  base::Optional<StackProfileTracker::SourceCallstack> GetCallstack(
+      StackProfileTracker::SourceCallstackId iid) const override {
+    auto* decoder = seq_state_->LookupInternedMessage<
+        protos::pbzero::InternedData::kCallstacksFieldNumber,
+        protos::pbzero::Callstack>(iid);
+    if (!decoder)
+      return base::nullopt;
+    return ProfilePacketUtils::MakeSourceCallstack(*decoder);
+  }
+
+ private:
+  PacketSequenceStateGeneration* seq_state_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROFILE_PACKET_UTILS_H_
diff --git a/src/trace_processor/importers/proto/proto_importer_module.cc b/src/trace_processor/importers/proto/proto_importer_module.cc
index 4524384..4f23f21 100644
--- a/src/trace_processor/importers/proto/proto_importer_module.cc
+++ b/src/trace_processor/importers/proto/proto_importer_module.cc
@@ -15,7 +15,7 @@
  */
 
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
-#include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 namespace perfetto {
 namespace trace_processor {
diff --git a/src/trace_processor/importers/proto/proto_importer_module.h b/src/trace_processor/importers/proto/proto_importer_module.h
index 60a0218..909eb14 100644
--- a/src/trace_processor/importers/proto/proto_importer_module.h
+++ b/src/trace_processor/importers/proto/proto_importer_module.h
@@ -45,8 +45,8 @@
 //     methods.
 // (2) In the constructor call the RegisterForField method for every field
 //     that the module knows how to handle.
-// (3) Create a module instance and add it to the |modules| vector in
-//     TraceProcessorContext.
+// (3) Create a module instance and add it to TraceProcessorContext's |modules|
+//     vector in either default_modules.cc or additional_modules.cc.
 // See GraphicsEventModule for an example.
 
 class ModuleResult {
@@ -120,6 +120,8 @@
   // stage, on all existing modules.
   virtual void ParseTraceConfig(const protos::pbzero::TraceConfig_Decoder&);
 
+  virtual void NotifyEndOfFile() {}
+
  protected:
   void RegisterForField(uint32_t field_id, TraceProcessorContext*);
 };
diff --git a/src/trace_processor/importers/proto/proto_trace_parser.cc b/src/trace_processor/importers/proto/proto_trace_parser.cc
index 4c5566d..796b318 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser.cc
@@ -29,20 +29,22 @@
 #include "perfetto/ext/base/utils.h"
 #include "perfetto/ext/base/uuid.h"
 #include "perfetto/trace_processor/status.h"
-#include "src/trace_processor/args_tracker.h"
-#include "src/trace_processor/clock_tracker.h"
-#include "src/trace_processor/event_tracker.h"
-#include "src/trace_processor/heap_profile_tracker.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/clock_tracker.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
 #include "src/trace_processor/importers/ftrace/ftrace_module.h"
+#include "src/trace_processor/importers/proto/heap_profile_tracker.h"
+#include "src/trace_processor/importers/proto/metadata_tracker.h"
 #include "src/trace_processor/importers/proto/packet_sequence_state.h"
-#include "src/trace_processor/metadata.h"
-#include "src/trace_processor/metadata_tracker.h"
-#include "src/trace_processor/process_tracker.h"
-#include "src/trace_processor/slice_tracker.h"
-#include "src/trace_processor/stack_profile_tracker.h"
+#include "src/trace_processor/importers/proto/perf_sample_tracker.h"
+#include "src/trace_processor/importers/proto/profile_packet_utils.h"
+#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
+#include "src/trace_processor/storage/metadata.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 "src/trace_processor/types/trace_processor_context.h"
 #include "src/trace_processor/types/variadic.h"
 
 #include "protos/perfetto/common/trace_stats.pbzero.h"
@@ -52,116 +54,17 @@
 #include "protos/perfetto/trace/clock_snapshot.pbzero.h"
 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
 #include "protos/perfetto/trace/perfetto/perfetto_metatrace.pbzero.h"
+#include "protos/perfetto/trace/perfetto/tracing_service_event.pbzero.h"
 #include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
 #include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
+#include "protos/perfetto/trace/profiling/smaps.pbzero.h"
 #include "protos/perfetto/trace/trace.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "protos/perfetto/trace/trigger.pbzero.h"
 
 namespace perfetto {
 namespace trace_processor {
 
-namespace {
-
-StackProfileTracker::SourceMapping MakeSourceMapping(
-    const protos::pbzero::Mapping::Decoder& entry) {
-  StackProfileTracker::SourceMapping src_mapping{};
-  src_mapping.build_id = entry.build_id();
-  src_mapping.exact_offset = entry.exact_offset();
-  src_mapping.start_offset = entry.start_offset();
-  src_mapping.start = entry.start();
-  src_mapping.end = entry.end();
-  src_mapping.load_bias = entry.load_bias();
-  for (auto path_string_id_it = entry.path_string_ids(); path_string_id_it;
-       ++path_string_id_it)
-    src_mapping.name_ids.emplace_back(*path_string_id_it);
-  return src_mapping;
-}
-
-StackProfileTracker::SourceFrame MakeSourceFrame(
-    const protos::pbzero::Frame::Decoder& entry) {
-  StackProfileTracker::SourceFrame src_frame;
-  src_frame.name_id = entry.function_name_id();
-  src_frame.mapping_id = entry.mapping_id();
-  src_frame.rel_pc = entry.rel_pc();
-  return src_frame;
-}
-
-StackProfileTracker::SourceCallstack MakeSourceCallstack(
-    const protos::pbzero::Callstack::Decoder& entry) {
-  StackProfileTracker::SourceCallstack src_callstack;
-  for (auto frame_it = entry.frame_ids(); frame_it; ++frame_it)
-    src_callstack.emplace_back(*frame_it);
-  return src_callstack;
-}
-
-class ProfilePacketInternLookup : public StackProfileTracker::InternLookup {
- public:
-  ProfilePacketInternLookup(PacketSequenceStateGeneration* seq_state)
-      : seq_state_(seq_state) {}
-
-  base::Optional<base::StringView> GetString(
-      StackProfileTracker::SourceStringId iid,
-      StackProfileTracker::InternedStringType type) const override {
-    protos::pbzero::InternedString::Decoder* decoder = nullptr;
-    switch (type) {
-      case StackProfileTracker::InternedStringType::kBuildId:
-        decoder = seq_state_->LookupInternedMessage<
-            protos::pbzero::InternedData::kBuildIdsFieldNumber,
-            protos::pbzero::InternedString>(iid);
-        break;
-      case StackProfileTracker::InternedStringType::kFunctionName:
-        decoder = seq_state_->LookupInternedMessage<
-            protos::pbzero::InternedData::kFunctionNamesFieldNumber,
-            protos::pbzero::InternedString>(iid);
-        break;
-      case StackProfileTracker::InternedStringType::kMappingPath:
-        decoder = seq_state_->LookupInternedMessage<
-            protos::pbzero::InternedData::kMappingPathsFieldNumber,
-            protos::pbzero::InternedString>(iid);
-        break;
-    }
-    if (!decoder)
-      return base::nullopt;
-    return base::StringView(reinterpret_cast<const char*>(decoder->str().data),
-                            decoder->str().size);
-  }
-
-  base::Optional<StackProfileTracker::SourceMapping> GetMapping(
-      StackProfileTracker::SourceMappingId iid) const override {
-    auto* decoder = seq_state_->LookupInternedMessage<
-        protos::pbzero::InternedData::kMappingsFieldNumber,
-        protos::pbzero::Mapping>(iid);
-    if (!decoder)
-      return base::nullopt;
-    return MakeSourceMapping(*decoder);
-  }
-
-  base::Optional<StackProfileTracker::SourceFrame> GetFrame(
-      StackProfileTracker::SourceFrameId iid) const override {
-    auto* decoder = seq_state_->LookupInternedMessage<
-        protos::pbzero::InternedData::kFramesFieldNumber,
-        protos::pbzero::Frame>(iid);
-    if (!decoder)
-      return base::nullopt;
-    return MakeSourceFrame(*decoder);
-  }
-
-  base::Optional<StackProfileTracker::SourceCallstack> GetCallstack(
-      StackProfileTracker::SourceCallstackId iid) const override {
-    auto* decoder = seq_state_->LookupInternedMessage<
-        protos::pbzero::InternedData::kCallstacksFieldNumber,
-        protos::pbzero::Callstack>(iid);
-    if (!decoder)
-      return base::nullopt;
-    return MakeSourceCallstack(*decoder);
-  }
-
- private:
-  PacketSequenceStateGeneration* seq_state_;
-};
-
-}  // namespace
-
 ProtoTraceParser::ProtoTraceParser(TraceProcessorContext* context)
     : context_(context),
       metatrace_id_(context->storage->InternString("metatrace")),
@@ -224,9 +127,8 @@
                        packet.profile_packet());
   }
 
-  if (packet.has_streaming_profile_packet()) {
-    ParseStreamingProfilePacket(data->sequence_state,
-                                packet.streaming_profile_packet());
+  if (packet.has_perf_sample()) {
+    ParsePerfSample(ts, data->sequence_state, packet.perf_sample());
   }
 
   if (packet.has_chrome_benchmark_metadata()) {
@@ -248,6 +150,18 @@
   if (packet.has_module_symbols()) {
     ParseModuleSymbols(packet.module_symbols());
   }
+
+  if (packet.has_trigger()) {
+    ParseTrigger(ts, packet.trigger());
+  }
+
+  if (packet.has_service_event()) {
+    ParseServiceEvent(ts, packet.service_event());
+  }
+
+  if (packet.has_smaps_packet()) {
+    ParseSmapsPacket(ts, packet.smaps_packet());
+  }
 }
 
 void ProtoTraceParser::ParseFtracePacket(uint32_t cpu,
@@ -346,14 +260,16 @@
 
   for (auto it = packet.mappings(); it; ++it) {
     protos::pbzero::Mapping::Decoder entry(*it);
-    StackProfileTracker::SourceMapping src_mapping = MakeSourceMapping(entry);
+    StackProfileTracker::SourceMapping src_mapping =
+        ProfilePacketUtils::MakeSourceMapping(entry);
     sequence_state->state()->stack_profile_tracker().AddMapping(entry.iid(),
                                                                 src_mapping);
   }
 
   for (auto it = packet.frames(); it; ++it) {
     protos::pbzero::Frame::Decoder entry(*it);
-    StackProfileTracker::SourceFrame src_frame = MakeSourceFrame(entry);
+    StackProfileTracker::SourceFrame src_frame =
+        ProfilePacketUtils::MakeSourceFrame(entry);
     sequence_state->state()->stack_profile_tracker().AddFrame(entry.iid(),
                                                               src_frame);
   }
@@ -361,7 +277,7 @@
   for (auto it = packet.callstacks(); it; ++it) {
     protos::pbzero::Callstack::Decoder entry(*it);
     StackProfileTracker::SourceCallstack src_callstack =
-        MakeSourceCallstack(entry);
+        ProfilePacketUtils::MakeSourceCallstack(entry);
     sequence_state->state()->stack_profile_tracker().AddCallstack(
         entry.iid(), src_callstack);
   }
@@ -382,6 +298,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);
@@ -391,6 +310,9 @@
     if (entry.rejected_concurrent())
       context_->storage->IncrementIndexedStats(
           stats::heapprofd_rejected_concurrent, pid);
+    if (entry.hit_guardrail())
+      context_->storage->IncrementIndexedStats(stats::heapprofd_hit_guardrail,
+                                               pid);
 
     for (auto sample_it = entry.samples(); sample_it; ++sample_it) {
       protos::pbzero::ProfilePacket::HeapSample::Decoder sample(*sample_it);
@@ -420,47 +342,55 @@
   }
 }
 
-void ProtoTraceParser::ParseStreamingProfilePacket(
+void ProtoTraceParser::ParsePerfSample(
+    int64_t ts,
     PacketSequenceStateGeneration* sequence_state,
     ConstBytes blob) {
-  protos::pbzero::StreamingProfilePacket::Decoder packet(blob.data, blob.size);
+  using PerfSample = protos::pbzero::PerfSample;
+  PerfSample::Decoder sample(blob.data, blob.size);
 
-  ProcessTracker* procs = context_->process_tracker.get();
-  TraceStorage* storage = context_->storage.get();
-  StackProfileTracker& stack_profile_tracker =
+  // Not a sample, but an indication of data loss in the ring buffer shared with
+  // the kernel.
+  if (sample.kernel_records_lost() > 0) {
+    PERFETTO_DCHECK(sample.pid() == 0);
+
+    context_->storage->IncrementIndexedStats(
+        stats::perf_cpu_lost_records, static_cast<int>(sample.cpu()),
+        static_cast<int64_t>(sample.kernel_records_lost()));
+    return;
+  }
+
+  // Sample that looked relevant for the tracing session, but had to be skipped.
+  // Either we failed to look up the procfs file descriptors necessary for
+  // remote stack unwinding (not unexpected in most cases), or the unwind queue
+  // was out of capacity (producer lost data on its own).
+  if (sample.has_sample_skipped_reason()) {
+    context_->storage->IncrementStats(stats::perf_samples_skipped);
+
+    if (sample.sample_skipped_reason() ==
+        PerfSample::PROFILER_SKIP_UNWIND_ENQUEUE)
+      context_->storage->IncrementStats(stats::perf_samples_skipped_dataloss);
+
+    return;
+  }
+
+  uint64_t callstack_iid = sample.callstack_iid();
+  StackProfileTracker& stack_tracker =
       sequence_state->state()->stack_profile_tracker();
   ProfilePacketInternLookup intern_lookup(sequence_state);
 
-  uint32_t pid = static_cast<uint32_t>(sequence_state->state()->pid());
-  uint32_t tid = static_cast<uint32_t>(sequence_state->state()->tid());
-  UniqueTid utid = procs->UpdateThread(tid, pid);
-
-  auto timestamp_it = packet.timestamp_delta_us();
-  for (auto callstack_it = packet.callstack_iid(); callstack_it;
-       ++callstack_it, ++timestamp_it) {
-    if (!timestamp_it) {
-      context_->storage->IncrementStats(stats::stackprofile_parser_error);
-      PERFETTO_ELOG(
-          "StreamingProfilePacket has less callstack IDs than timestamps!");
-      break;
-    }
-
-    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;
-    }
-
-    uint32_t callstack_id = maybe_callstack_id->value;
-
-    tables::CpuProfileStackSampleTable::Row sample_row{
-        sequence_state->state()->IncrementAndGetTrackEventTimeNs(*timestamp_it *
-                                                                 1000),
-        callstack_id, utid};
-    storage->mutable_cpu_profile_stack_sample_table()->Insert(sample_row);
+  base::Optional<CallsiteId> cs_id =
+      stack_tracker.FindOrInsertCallstack(callstack_iid, &intern_lookup);
+  if (!cs_id) {
+    context_->storage->IncrementStats(stats::stackprofile_parser_error);
+    PERFETTO_ELOG("PerfSample referencing invalid callstack iid [%" PRIu64
+                  "] at timestamp [%" PRIi64 "]",
+                  callstack_iid, ts);
+    return;
   }
+
+  context_->perf_sample_tracker->AddStackToSliceTrack(
+      ts, *cs_id, sample.pid(), sample.tid(), sample.cpu());
 }
 
 void ProtoTraceParser::ParseChromeBenchmarkMetadata(ConstBytes blob) {
@@ -517,8 +447,9 @@
   protos::pbzero::ChromeEventBundle::Decoder bundle(blob.data, blob.size);
   ArgsTracker args(context_);
   if (bundle.has_metadata()) {
-    RawId id = storage->mutable_raw_table()->Insert(
-        {ts, raw_chrome_metadata_event_id_, 0, 0});
+    RawId id = storage->mutable_raw_table()
+                   ->Insert({ts, raw_chrome_metadata_event_id_, 0, 0})
+                   .id;
     auto inserter = args.AddArgsTo(id);
 
     // Metadata is proxied via a special event in the raw table to JSON export.
@@ -544,8 +475,10 @@
   }
 
   if (bundle.has_legacy_ftrace_output()) {
-    RawId id = storage->mutable_raw_table()->Insert(
-        {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})
+            .id;
 
     std::string data;
     for (auto it = bundle.legacy_ftrace_output(); it; ++it) {
@@ -563,8 +496,10 @@
           protos::pbzero::ChromeLegacyJsonTrace::USER_TRACE) {
         continue;
       }
-      RawId id = storage->mutable_raw_table()->Insert(
-          {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})
+              .id;
       Variadic value =
           Variadic::String(storage->InternString(legacy_trace.data()));
       args.AddArgsTo(id).AddArg(data_name_id_, value);
@@ -629,11 +564,18 @@
 
 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);
+  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()),
-      context_->storage->InternString(base::StringView(hex_build_id)));
+      context_->storage->InternString(module_symbols.path()), build_id);
   if (mapping_ids.empty()) {
     context_->storage->IncrementStats(stats::stackprofile_invalid_mapping_id);
     return;
@@ -670,5 +612,48 @@
   }
 }
 
+void ProtoTraceParser::ParseTrigger(int64_t ts, ConstBytes blob) {
+  protos::pbzero::Trigger::Decoder trigger(blob.data, blob.size);
+  StringId cat_id = kNullStringId;
+  TrackId track_id = context_->track_tracker->GetOrCreateTriggerTrack();
+  StringId name_id = context_->storage->InternString(trigger.trigger_name());
+  context_->slice_tracker->Scoped(
+      ts, track_id, cat_id, name_id,
+      /* duration = */ 0,
+      [&trigger, this](ArgsTracker::BoundInserter* args_table) {
+        StringId producer_name_key =
+            context_->storage->InternString("producer_name");
+        args_table->AddArg(producer_name_key,
+                           Variadic::String(context_->storage->InternString(
+                               trigger.producer_name())));
+        StringId trusted_producer_uid_key =
+            context_->storage->InternString("trusted_producer_uid");
+        args_table->AddArg(trusted_producer_uid_key,
+                           Variadic::Integer(trigger.trusted_producer_uid()));
+      });
+}
+
+void ProtoTraceParser::ParseServiceEvent(int64_t ts, ConstBytes blob) {
+  protos::pbzero::TracingServiceEvent::Decoder tse(blob.data, blob.size);
+  if (tse.all_data_sources_started()) {
+    context_->metadata_tracker->SetMetadata(
+        metadata::all_data_source_started_ns, Variadic::Integer(ts));
+  }
+}
+
+void ProtoTraceParser::ParseSmapsPacket(int64_t ts, ConstBytes blob) {
+  protos::pbzero::SmapsPacket::Decoder sp(blob.data, blob.size);
+  auto upid = context_->process_tracker->GetOrCreateProcess(sp.pid());
+
+  for (auto it = sp.entries(); it; ++it) {
+    protos::pbzero::SmapsEntry::Decoder e(*it);
+    context_->storage->mutable_profiler_smaps_table()->Insert(
+        {upid, ts, context_->storage->InternString(e.path()),
+         static_cast<int64_t>(e.size_kb()),
+         static_cast<int64_t>(e.private_dirty_kb()),
+         static_cast<int64_t>(e.swap_kb())});
+  }
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/proto_trace_parser.h b/src/trace_processor/importers/proto/proto_trace_parser.h
index 9e20b3e..87bdcfc 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser.h
+++ b/src/trace_processor/importers/proto/proto_trace_parser.h
@@ -25,10 +25,10 @@
 #include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/protozero/field.h"
+#include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/timestamped_trace_piece.h"
 #include "src/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/trace_parser.h"
-#include "src/trace_processor/trace_storage.h"
 
 namespace perfetto {
 
@@ -66,12 +66,15 @@
                           PacketSequenceStateGeneration*,
                           uint32_t seq_id,
                           ConstBytes);
-  void ParseStreamingProfilePacket(PacketSequenceStateGeneration*, ConstBytes);
+  void ParsePerfSample(int64_t ts, PacketSequenceStateGeneration*, ConstBytes);
   void ParseChromeBenchmarkMetadata(ConstBytes);
   void ParseChromeEvents(int64_t ts, ConstBytes);
   void ParseMetatraceEvent(int64_t ts, ConstBytes);
   void ParseTraceConfig(ConstBytes);
   void ParseModuleSymbols(ConstBytes);
+  void ParseTrigger(int64_t ts, ConstBytes);
+  void ParseServiceEvent(int64_t ts, ConstBytes);
+  void ParseSmapsPacket(int64_t ts, ConstBytes);
 
  private:
   TraceProcessorContext* context_;
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 ff27f76..81c9982 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
@@ -19,23 +19,21 @@
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
-#include "src/trace_processor/args_tracker.h"
-#include "src/trace_processor/clock_tracker.h"
-#include "src/trace_processor/event_tracker.h"
-#include "src/trace_processor/importers/ftrace/ftrace_module.h"
+#include "src/trace_processor/importers/additional_modules.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/clock_tracker.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/importers/default_modules.h"
 #include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
-#include "src/trace_processor/importers/proto/proto_importer_module.h"
+#include "src/trace_processor/importers/proto/metadata_tracker.h"
 #include "src/trace_processor/importers/proto/proto_trace_parser.h"
-#include "src/trace_processor/importers/proto/track_event_module.h"
-#include "src/trace_processor/metadata.h"
-#include "src/trace_processor/metadata_tracker.h"
-#include "src/trace_processor/process_tracker.h"
-#include "src/trace_processor/register_additional_modules.h"
-#include "src/trace_processor/slice_tracker.h"
-#include "src/trace_processor/stack_profile_tracker.h"
+#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
+#include "src/trace_processor/storage/metadata.h"
+#include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/trace_sorter.h"
-#include "src/trace_processor/trace_storage.h"
-#include "src/trace_processor/track_tracker.h"
 #include "test/gtest_and_gmock.h"
 
 #include "protos/perfetto/common/sys_stats_counters.pbzero.h"
@@ -157,14 +155,20 @@
 
 class MockBoundInserter : public ArgsTracker::BoundInserter {
  public:
-  MockBoundInserter() : ArgsTracker::BoundInserter(nullptr, nullptr, 0u) {
-    ON_CALL(*this, AddArg(_, _, _)).WillByDefault(ReturnRef(*this));
+  MockBoundInserter()
+      : ArgsTracker::BoundInserter(&tracker_, nullptr, 0u), tracker_(nullptr) {
+    ON_CALL(*this, AddArg(_, _, _, _)).WillByDefault(ReturnRef(*this));
   }
 
-  MOCK_METHOD3(AddArg,
-               ArgsTracker::BoundInserter&(StringId flat_key,
-                                           StringId key,
-                                           Variadic v));
+  MOCK_METHOD4(
+      AddArg,
+      ArgsTracker::BoundInserter&(StringId flat_key,
+                                  StringId key,
+                                  Variadic v,
+                                  ArgsTracker::UpdatePolicy update_policy));
+
+ private:
+  ArgsTracker tracker_;
 };
 
 class MockSliceTracker : public SliceTracker {
@@ -211,10 +215,9 @@
     context_.slice_tracker.reset(slice_);
     clock_ = new ClockTracker(&context_);
     context_.clock_tracker.reset(clock_);
-    context_.sorter.reset(new TraceSorter(&context_, 0 /*window size*/));
-    context_.parser.reset(new ProtoTraceParser(&context_));
-    context_.modules.emplace_back(new TrackEventModule(&context_));
+    context_.sorter.reset(new TraceSorter(CreateParser(), 0 /*window size*/));
 
+    RegisterDefaultModules(&context_);
     RegisterAdditionalModules(&context_);
   }
 
@@ -257,6 +260,10 @@
   }
 
  protected:
+  std::unique_ptr<TraceParser> CreateParser() {
+    return std::unique_ptr<TraceParser>(new ProtoTraceParser(&context_));
+  }
+
   std::unique_ptr<protozero::ScatteredHeapBuffer> heap_buf_;
   std::unique_ptr<protozero::ScatteredStreamWriter> stream_writer_;
   protos::pbzero::Trace trace_;
@@ -330,14 +337,19 @@
   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);
+  // Order is by row and then by StringIds.
+  ASSERT_EQ(args.key()[0], context_.storage->InternString("comm"));
+  ASSERT_EQ(args.key()[1], context_.storage->InternString("pid"));
+  ASSERT_EQ(args.key()[2], context_.storage->InternString("oom_score_adj"));
+  ASSERT_EQ(args.key()[3], context_.storage->InternString("clone_flags"));
+  ASSERT_EQ(args.key()[4], context_.storage->InternString("ip"));
+  ASSERT_EQ(args.key()[5], context_.storage->InternString("buf"));
+  ASSERT_STREQ(args.string_value().GetString(0).c_str(), task_newtask);
+  ASSERT_EQ(args.int_value()[1], 123);
+  ASSERT_EQ(args.int_value()[2], 15);
+  ASSERT_EQ(args.int_value()[3], 12);
   ASSERT_EQ(args.int_value()[4], 20);
-  ASSERT_STREQ(context_.storage->GetString(args.string_value()[5]).c_str(),
-               buf_value);
+  ASSERT_STREQ(args.string_value().GetString(5).c_str(), buf_value);
 
   // TODO(taylori): Add test ftrace event with all field types
   // and test here.
@@ -617,7 +629,7 @@
 
 TEST_F(ProtoTraceParserTest, ProcessNameFromProcessDescriptor) {
   context_.sorter.reset(new TraceSorter(
-      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+      CreateParser(), std::numeric_limits<int64_t>::max() /*window size*/));
   {
     auto* packet = trace_.add_packet();
     packet->set_trusted_packet_sequence_id(1);
@@ -662,7 +674,7 @@
 
 TEST_F(ProtoTraceParserTest, ThreadNameFromThreadDescriptor) {
   context_.sorter.reset(new TraceSorter(
-      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+      CreateParser(), std::numeric_limits<int64_t>::max() /*window size*/));
   {
     auto* packet = trace_.add_packet();
     packet->set_trusted_packet_sequence_id(1);
@@ -716,7 +728,7 @@
 
 TEST_F(ProtoTraceParserTest, TrackEventWithoutInternedData) {
   context_.sorter.reset(new TraceSorter(
-      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+      CreateParser(), std::numeric_limits<int64_t>::max() /*window size*/));
 
   {
     auto* packet = trace_.add_packet();
@@ -776,14 +788,26 @@
 
   MockBoundInserter inserter;
 
+  StringId unknown_cat = storage_->InternString("unknown(1)");
+
   constexpr TrackId track{0u};
+  constexpr TrackId thread_time_track{1u};
+
   InSequence in_sequence;  // Below slices should be sorted by timestamp.
+  EXPECT_CALL(*event_, PushCounter(1005000, testing::DoubleEq(2003000),
+                                   thread_time_track));
+  EXPECT_CALL(*event_, PushCounter(1028000, testing::DoubleEq(2015000),
+                                   thread_time_track));
   EXPECT_CALL(*slice_,
               Scoped(1005000, track, kNullStringId, kNullStringId, 23000, _))
       .WillOnce(DoAll(InvokeArgument<5>(&inserter), Return(0u)));
-  EXPECT_CALL(*slice_, Begin(1010000, track, kNullStringId, kNullStringId, _))
+  EXPECT_CALL(*event_, PushCounter(1010000, testing::DoubleEq(2005000),
+                                   thread_time_track));
+  EXPECT_CALL(*slice_, Begin(1010000, track, unknown_cat, kNullStringId, _))
       .WillOnce(DoAll(InvokeArgument<4>(&inserter), Return(1u)));
-  EXPECT_CALL(*slice_, End(1020000, track, kNullStringId, kNullStringId, _))
+  EXPECT_CALL(*event_, PushCounter(1020000, testing::DoubleEq(2010000),
+                                   thread_time_track));
+  EXPECT_CALL(*slice_, End(1020000, track, unknown_cat, kNullStringId, _))
       .WillOnce(DoAll(InvokeArgument<4>(&inserter), Return(1u)));
 
   context_.sorter->ExtractEventsForced();
@@ -799,7 +823,7 @@
 
 TEST_F(ProtoTraceParserTest, TrackEventWithoutInternedDataWithTypes) {
   context_.sorter.reset(new TraceSorter(
-      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+      CreateParser(), std::numeric_limits<int64_t>::max() /*window size*/));
 
   {
     auto* packet = trace_.add_packet();
@@ -856,14 +880,25 @@
 
   MockBoundInserter inserter;
 
+  StringId unknown_cat1 = storage_->InternString("unknown(1)");
+  StringId unknown_cat2 = storage_->InternString("unknown(2)");
+
   constexpr TrackId track{0u};
+  constexpr TrackId thread_time_track{1u};
+
   InSequence in_sequence;  // Below slices should be sorted by timestamp.
-  EXPECT_CALL(*slice_, Begin(1010000, track, kNullStringId, kNullStringId, _))
+  EXPECT_CALL(*event_, PushCounter(1010000, testing::DoubleEq(2005000),
+                                   thread_time_track));
+  EXPECT_CALL(*slice_, Begin(1010000, track, unknown_cat1, kNullStringId, _))
       .WillOnce(DoAll(InvokeArgument<4>(&inserter), Return(0u)));
+  EXPECT_CALL(*event_, PushCounter(1015000, testing::DoubleEq(2007000),
+                                   thread_time_track));
   EXPECT_CALL(*slice_,
-              Scoped(1015000, track, kNullStringId, kNullStringId, 0, _))
+              Scoped(1015000, track, unknown_cat2, kNullStringId, 0, _))
       .WillOnce(DoAll(InvokeArgument<5>(&inserter), Return(1u)));
-  EXPECT_CALL(*slice_, End(1020000, track, kNullStringId, kNullStringId, _))
+  EXPECT_CALL(*event_, PushCounter(1020000, testing::DoubleEq(2010000),
+                                   thread_time_track));
+  EXPECT_CALL(*slice_, End(1020000, track, unknown_cat1, kNullStringId, _))
       .WillOnce(DoAll(InvokeArgument<4>(&inserter), Return(0u)));
 
   context_.sorter->ExtractEventsForced();
@@ -879,7 +914,7 @@
 
 TEST_F(ProtoTraceParserTest, TrackEventWithInternedData) {
   context_.sorter.reset(new TraceSorter(
-      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+      CreateParser(), std::numeric_limits<int64_t>::max() /*window size*/));
 
   {
     auto* packet = trace_.add_packet();
@@ -990,7 +1025,9 @@
   storage_->mutable_thread_table()->Insert(row);
 
   constexpr TrackId thread_1_track{0u};
-  constexpr TrackId process_2_track{1u};
+  constexpr TrackId thread_time_track{1u};
+  constexpr TrackId thread_instruction_count_track{2u};
+  constexpr TrackId process_2_track{3u};
 
   StringId cat_2_3 = storage_->InternString("cat2,cat3");
   StringId ev_2 = storage_->InternString("ev2");
@@ -1000,25 +1037,45 @@
   InSequence in_sequence;  // Below slices should be sorted by timestamp.
 
   MockBoundInserter inserter;
+  EXPECT_CALL(*event_, PushCounter(1005000, testing::DoubleEq(2003000),
+                                   thread_time_track));
+  EXPECT_CALL(*event_, PushCounter(1028000, testing::DoubleEq(2015000),
+                                   thread_time_track));
+  EXPECT_CALL(*event_, PushCounter(1005000, testing::DoubleEq(3010),
+                                   thread_instruction_count_track));
+  EXPECT_CALL(*event_, PushCounter(1028000, testing::DoubleEq(3060),
+                                   thread_instruction_count_track));
   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(inserter, AddArg(_, _, Variadic::UnsignedInteger(9999u), _));
+  EXPECT_CALL(inserter, AddArg(_, _, Variadic::Boolean(true), _));
+  EXPECT_CALL(inserter, AddArg(_, _, _, _));
 
+  EXPECT_CALL(*event_, PushCounter(1010000, testing::DoubleEq(2005000),
+                                   thread_time_track));
+  EXPECT_CALL(*event_, PushCounter(1010000, testing::DoubleEq(3020),
+                                   thread_instruction_count_track));
   EXPECT_CALL(*slice_, Begin(1010000, thread_1_track, cat_1, ev_1, _))
       .WillOnce(DoAll(InvokeArgument<4>(&inserter), Return(1u)));
 
+  EXPECT_CALL(*event_, PushCounter(1020000, testing::DoubleEq(2010000),
+                                   thread_time_track));
+  EXPECT_CALL(*event_, PushCounter(1020000, testing::DoubleEq(3040),
+                                   thread_instruction_count_track));
   EXPECT_CALL(*slice_, End(1020000, thread_1_track, cat_1, ev_1, _))
       .WillOnce(DoAll(InvokeArgument<4>(&inserter), Return(1u)));
 
+  EXPECT_CALL(*event_, PushCounter(1040000, testing::DoubleEq(2030000),
+                                   thread_time_track));
+  EXPECT_CALL(*event_, PushCounter(1040000, testing::DoubleEq(3100),
+                                   thread_instruction_count_track));
   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, cat_1, ev_1, 0, _))
       .WillOnce(DoAll(InvokeArgument<5>(&inserter), Return(3u)));
   // Second slice should have a legacy_event.passthrough_utid arg.
-  EXPECT_CALL(inserter, AddArg(_, _, Variadic::UnsignedInteger(1u)));
+  EXPECT_CALL(inserter, AddArg(_, _, Variadic::UnsignedInteger(1u), _));
 
   context_.sorter->ExtractEventsForced();
 
@@ -1042,7 +1099,7 @@
 
 TEST_F(ProtoTraceParserTest, TrackEventAsyncEvents) {
   context_.sorter.reset(new TraceSorter(
-      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+      CreateParser(), std::numeric_limits<int64_t>::max() /*window size*/));
 
   {
     auto* packet = trace_.add_packet();
@@ -1150,23 +1207,35 @@
   StringId cat_2 = storage_->InternString("cat2");
   StringId ev_2 = storage_->InternString("ev2");
 
+  TrackId thread_time_track{2u};
+  TrackId thread_instruction_count_track{3u};
+
   InSequence in_sequence;  // Below slices should be sorted by timestamp.
 
+  EXPECT_CALL(*event_, PushCounter(1010000, testing::DoubleEq(2005000),
+                                   thread_time_track));
+  EXPECT_CALL(*event_, PushCounter(1010000, testing::DoubleEq(3020),
+                                   thread_instruction_count_track));
   EXPECT_CALL(*slice_, Begin(1010000, TrackId{1}, cat_1, ev_1, _))
       .WillOnce(Return(0u));
   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_, Scoped(1018000, TrackId{4}, cat_2, ev_2, 0, _));
+  EXPECT_CALL(*event_, PushCounter(1020000, testing::DoubleEq(2010000),
+                                   thread_time_track));
+  EXPECT_CALL(*event_, PushCounter(1020000, testing::DoubleEq(3040),
+                                   thread_instruction_count_track));
   EXPECT_CALL(*slice_, End(1020000, TrackId{1}, cat_1, ev_1, _))
       .WillOnce(Return(0u));
-  EXPECT_CALL(*slice_, Scoped(1030000, TrackId{3}, cat_2, ev_2, 0, _));
+  EXPECT_CALL(*slice_, Scoped(1030000, TrackId{5}, 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);
+  // First track is for the thread; second first async, third and fourth for
+  // thread time and instruction count, others are the async event tracks.
+  EXPECT_EQ(storage_->track_table().row_count(), 6u);
   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_->track_table().name()[4], ev_2);
+  EXPECT_EQ(storage_->track_table().name()[5], ev_2);
 
   EXPECT_EQ(storage_->process_track_table().row_count(), 3u);
   EXPECT_EQ(storage_->process_track_table().upid()[0], 1u);
@@ -1186,7 +1255,7 @@
 // TODO(eseckler): Also test instant events on separate tracks.
 TEST_F(ProtoTraceParserTest, TrackEventWithTrackDescriptors) {
   context_.sorter.reset(new TraceSorter(
-      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+      CreateParser(), std::numeric_limits<int64_t>::max() /*window size*/));
 
   // Sequence 1.
   {
@@ -1325,10 +1394,14 @@
   EXPECT_CALL(*slice_, Begin(1010000, TrackId{1}, cat_1, ev_1, _))
       .WillOnce(Return(0u));
 
+  EXPECT_CALL(*event_,
+              PushCounter(1015000, testing::DoubleEq(2007000), TrackId{4}));
   EXPECT_CALL(*slice_, Scoped(1015000, TrackId{0}, cat_2, ev_2, 0, _))
       .WillOnce(Return(1u));
 
-  EXPECT_CALL(*slice_, Scoped(1016000, TrackId{2}, cat_3, ev_3, 0, _))
+  EXPECT_CALL(*event_,
+              PushCounter(1016000, testing::DoubleEq(2008000), TrackId{5}));
+  EXPECT_CALL(*slice_, Scoped(1016000, TrackId{3}, cat_3, ev_3, 0, _))
       .WillOnce(Return(2u));
 
   EXPECT_CALL(*slice_,
@@ -1337,12 +1410,14 @@
 
   context_.sorter->ExtractEventsForced();
 
-  // First track is "Thread track 1"; second is "Async track 1", third is
-  // "Thread track 2".
-  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", fifth &
+  // sixth are thread time tracks for thread 1 and 2.
+  EXPECT_EQ(storage_->track_table().row_count(), 6u);
   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), "Thread track 2");
+  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);
@@ -1371,7 +1446,7 @@
 
 TEST_F(ProtoTraceParserTest, TrackEventWithoutIncrementalStateReset) {
   context_.sorter.reset(new TraceSorter(
-      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+      CreateParser(), std::numeric_limits<int64_t>::max() /*window size*/));
 
   {
     auto* packet = trace_.add_packet();
@@ -1383,7 +1458,8 @@
     thread_desc->set_reference_thread_time_us(2000);
   }
   {
-    // Event should be discarded because incremental state was never cleared.
+    // Event should be discarded because delta timestamps require valid
+    // incremental state + thread descriptor.
     auto* packet = trace_.add_packet();
     packet->set_trusted_packet_sequence_id(1);
     auto* event = packet->set_track_event();
@@ -1394,19 +1470,48 @@
     legacy_event->set_name_iid(1);
     legacy_event->set_phase('B');
   }
+  {
+    // Event should be discarded because it specifies
+    // SEQ_NEEDS_INCREMENTAL_STATE.
+    auto* packet = trace_.add_packet();
+    packet->set_timestamp(2000000);
+    packet->set_trusted_packet_sequence_id(1);
+    packet->set_sequence_flags(
+        protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
+    auto* event = packet->set_track_event();
+    event->add_categories("cat");
+    event->set_name("ev1");
+    event->set_type(protos::pbzero::TrackEvent::TYPE_INSTANT);
+  }
+  {
+    // Event should be accepted because it does not specify
+    // SEQ_NEEDS_INCREMENTAL_STATE and uses absolute timestamps.
+    auto* packet = trace_.add_packet();
+    packet->set_timestamp(2100000);
+    packet->set_trusted_packet_sequence_id(1);
+    auto* event = packet->set_track_event();
+    event->add_categories("cat1");
+    event->set_name("ev2");
+    event->set_type(protos::pbzero::TrackEvent::TYPE_INSTANT);
+  }
 
   Tokenize();
 
-  EXPECT_CALL(*slice_, Begin(_, _, _, _, _)).Times(0);
+  StringId cat1 = storage_->InternString("cat1");
+  StringId ev2 = storage_->InternString("ev2");
+
+  EXPECT_CALL(*slice_, Scoped(2100000, TrackId{0}, cat1, ev2, 0, _))
+      .WillOnce(Return(0u));
   context_.sorter->ExtractEventsForced();
 }
 
 TEST_F(ProtoTraceParserTest, TrackEventWithoutThreadDescriptor) {
   context_.sorter.reset(new TraceSorter(
-      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+      CreateParser(), std::numeric_limits<int64_t>::max() /*window size*/));
 
   {
-    // Event should be discarded because no thread descriptor was seen yet.
+    // Event should be discarded because it specifies delta timestamps and no
+    // thread descriptor was seen yet.
     auto* packet = trace_.add_packet();
     packet->set_trusted_packet_sequence_id(1);
     packet->set_incremental_state_cleared(true);
@@ -1418,16 +1523,33 @@
     legacy_event->set_name_iid(1);
     legacy_event->set_phase('B');
   }
+  {
+    // Events that specify SEQ_NEEDS_INCREMENTAL_STATE should be accepted even
+    // if there's no valid thread descriptor.
+    auto* packet = trace_.add_packet();
+    packet->set_timestamp(2000000);
+    packet->set_trusted_packet_sequence_id(1);
+    packet->set_sequence_flags(
+        protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
+    auto* event = packet->set_track_event();
+    event->add_categories("cat1");
+    event->set_name("ev1");
+    event->set_type(protos::pbzero::TrackEvent::TYPE_INSTANT);
+  }
 
   Tokenize();
 
-  EXPECT_CALL(*slice_, Begin(_, _, _, _, _)).Times(0);
+  StringId cat1 = storage_->InternString("cat1");
+  StringId ev1 = storage_->InternString("ev1");
+
+  EXPECT_CALL(*slice_, Scoped(2000000, TrackId{0}, cat1, ev1, 0, _))
+      .WillOnce(Return(0u));
   context_.sorter->ExtractEventsForced();
 }
 
 TEST_F(ProtoTraceParserTest, TrackEventWithDataLoss) {
   context_.sorter.reset(new TraceSorter(
-      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+      CreateParser(), std::numeric_limits<int64_t>::max() /*window size*/));
 
   {
     auto* packet = trace_.add_packet();
@@ -1437,14 +1559,12 @@
     thread_desc->set_pid(15);
     thread_desc->set_tid(16);
     thread_desc->set_reference_timestamp_us(1000);
-    thread_desc->set_reference_thread_time_us(2000);
   }
   {
     auto* packet = trace_.add_packet();
     packet->set_trusted_packet_sequence_id(1);
     auto* event = packet->set_track_event();
     event->set_timestamp_delta_us(10);   // absolute: 1010.
-    event->set_thread_time_delta_us(5);  // absolute: 2005.
     event->add_category_iids(1);
     auto* legacy_event = event->set_legacy_event();
     legacy_event->set_name_iid(1);
@@ -1457,7 +1577,6 @@
     packet->set_previous_packet_dropped(true);  // Data loss occurred.
     auto* event = packet->set_track_event();
     event->set_timestamp_delta_us(10);
-    event->set_thread_time_delta_us(5);
     event->add_category_iids(1);
     auto* legacy_event = event->set_legacy_event();
     legacy_event->set_name_iid(1);
@@ -1469,7 +1588,6 @@
     packet->set_trusted_packet_sequence_id(1);
     auto* event = packet->set_track_event();
     event->set_timestamp_delta_us(10);
-    event->set_thread_time_delta_us(5);
     event->add_category_iids(1);
     auto* legacy_event = event->set_legacy_event();
     legacy_event->set_name_iid(1);
@@ -1482,7 +1600,6 @@
     packet->set_incremental_state_cleared(true);
     auto* event = packet->set_track_event();
     event->set_timestamp_delta_us(10);
-    event->set_thread_time_delta_us(5);
     event->add_category_iids(1);
     auto* legacy_event = event->set_legacy_event();
     legacy_event->set_name_iid(1);
@@ -1495,14 +1612,12 @@
     thread_desc->set_pid(15);
     thread_desc->set_tid(16);
     thread_desc->set_reference_timestamp_us(2000);
-    thread_desc->set_reference_thread_time_us(3000);
   }
   {
     auto* packet = trace_.add_packet();
     packet->set_trusted_packet_sequence_id(1);
     auto* event = packet->set_track_event();
     event->set_timestamp_delta_us(10);   // absolute: 2010.
-    event->set_thread_time_delta_us(5);  // absolute: 3005.
     event->add_category_iids(1);
     auto* legacy_event = event->set_legacy_event();
     legacy_event->set_name_iid(1);
@@ -1511,24 +1626,24 @@
 
   Tokenize();
 
-  EXPECT_CALL(*process_, UpdateThread(16, 15))
-      .WillRepeatedly(Return(1));
+  EXPECT_CALL(*process_, UpdateThread(16, 15)).WillRepeatedly(Return(1));
 
   tables::ThreadTable::Row row(16);
   row.upid = 1u;
   storage_->mutable_thread_table()->Insert(row);
 
+  StringId unknown_cat = storage_->InternString("unknown(1)");
   constexpr TrackId track{0u};
   InSequence in_sequence;  // Below slices should be sorted by timestamp.
-  EXPECT_CALL(*slice_, Begin(1010000, track, kNullStringId, kNullStringId, _));
-  EXPECT_CALL(*slice_, End(2010000, track, kNullStringId, kNullStringId, _));
+  EXPECT_CALL(*slice_, Begin(1010000, track, unknown_cat, kNullStringId, _));
+  EXPECT_CALL(*slice_, End(2010000, track, unknown_cat, kNullStringId, _));
 
   context_.sorter->ExtractEventsForced();
 }
 
 TEST_F(ProtoTraceParserTest, TrackEventMultipleSequences) {
   context_.sorter.reset(new TraceSorter(
-      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+      CreateParser(), std::numeric_limits<int64_t>::max() /*window size*/));
 
   {
     auto* packet = trace_.add_packet();
@@ -1538,14 +1653,12 @@
     thread_desc->set_pid(15);
     thread_desc->set_tid(16);
     thread_desc->set_reference_timestamp_us(1000);
-    thread_desc->set_reference_thread_time_us(2000);
   }
   {
     auto* packet = trace_.add_packet();
     packet->set_trusted_packet_sequence_id(1);
     auto* event = packet->set_track_event();
     event->set_timestamp_delta_us(10);   // absolute: 1010.
-    event->set_thread_time_delta_us(5);  // absolute: 2005.
     event->add_category_iids(1);
     auto* legacy_event = event->set_legacy_event();
     legacy_event->set_name_iid(1);
@@ -1567,14 +1680,12 @@
     thread_desc->set_pid(15);
     thread_desc->set_tid(17);
     thread_desc->set_reference_timestamp_us(995);
-    thread_desc->set_reference_thread_time_us(3000);
   }
   {
     auto* packet = trace_.add_packet();
     packet->set_trusted_packet_sequence_id(2);
     auto* event = packet->set_track_event();
     event->set_timestamp_delta_us(10);   // absolute: 1005.
-    event->set_thread_time_delta_us(5);  // absolute: 3005.
     event->add_category_iids(1);
     auto* legacy_event = event->set_legacy_event();
     legacy_event->set_name_iid(1);
@@ -1593,7 +1704,6 @@
     packet->set_trusted_packet_sequence_id(1);
     auto* event = packet->set_track_event();
     event->set_timestamp_delta_us(10);   // absolute: 1020.
-    event->set_thread_time_delta_us(5);  // absolute: 2010.
     event->add_category_iids(1);
     auto* legacy_event = event->set_legacy_event();
     legacy_event->set_name_iid(1);
@@ -1604,7 +1714,6 @@
     packet->set_trusted_packet_sequence_id(2);
     auto* event = packet->set_track_event();
     event->set_timestamp_delta_us(10);   // absolute: 1015.
-    event->set_thread_time_delta_us(5);  // absolute: 3015.
     event->add_category_iids(1);
     auto* legacy_event = event->set_legacy_event();
     legacy_event->set_name_iid(1);
@@ -1644,7 +1753,7 @@
 
 TEST_F(ProtoTraceParserTest, TrackEventWithDebugAnnotations) {
   context_.sorter.reset(new TraceSorter(
-      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+      CreateParser(), std::numeric_limits<int64_t>::max() /*window size*/));
   MockBoundInserter inserter;
 
   {
@@ -1655,14 +1764,12 @@
     thread_desc->set_pid(15);
     thread_desc->set_tid(16);
     thread_desc->set_reference_timestamp_us(1000);
-    thread_desc->set_reference_thread_time_us(2000);
   }
   {
     auto* packet = trace_.add_packet();
     packet->set_trusted_packet_sequence_id(1);
     auto* event = packet->set_track_event();
     event->set_timestamp_delta_us(10);   // absolute: 1010.
-    event->set_thread_time_delta_us(5);  // absolute: 2005.
     event->add_category_iids(1);
     auto* annotation1 = event->add_debug_annotations();
     annotation1->set_name_iid(1);
@@ -1715,7 +1822,6 @@
     packet->set_trusted_packet_sequence_id(1);
     auto* event = packet->set_track_event();
     event->set_timestamp_delta_us(10);   // absolute: 1020.
-    event->set_thread_time_delta_us(5);  // absolute: 2010.
     event->add_category_iids(1);
     auto* annotation3 = event->add_debug_annotations();
     annotation3->set_name_iid(3);
@@ -1734,7 +1840,8 @@
     annotation7->set_string_value("val7");
     auto* annotation8 = event->add_debug_annotations();
     annotation8->set_name_iid(8);
-    annotation8->set_legacy_json_value("val8");
+    annotation8->set_legacy_json_value(
+        "{\"val8\": {\"a\": 42, \"b\": \"val8b\"}, \"arr8\": [1, 2, 3]}");
     auto* annotation9 = event->add_debug_annotations();
     annotation9->set_name_iid(9);
     annotation9->set_int_value(15);
@@ -1768,8 +1875,7 @@
 
   Tokenize();
 
-  EXPECT_CALL(*process_, UpdateThread(16, 15))
-      .WillRepeatedly(Return(1));
+  EXPECT_CALL(*process_, UpdateThread(16, 15)).WillRepeatedly(Return(1));
 
   tables::ThreadTable::Row row(16);
   row.upid = 1u;
@@ -1790,50 +1896,66 @@
   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_val8_a = storage_->InternString("debug.an8.val8.a");
+  StringId debug_an_8_val8_b = storage_->InternString("debug.an8.val8.b");
+  StringId val_8b = storage_->InternString("val8b");
+  StringId debug_an_8_arr8 = storage_->InternString("debug.an8.arr8");
+  StringId debug_an_8_arr8_0 = storage_->InternString("debug.an8.arr8[0]");
+  StringId debug_an_8_arr8_1 = storage_->InternString("debug.an8.arr8[1]");
+  StringId debug_an_8_arr8_2 = storage_->InternString("debug.an8.arr8[2]");
   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, cat_1, ev_1, _))
       .WillOnce(DoAll(InvokeArgument<4>(&inserter), Return(1u)));
-  EXPECT_CALL(inserter,
-              AddArg(debug_an_1, debug_an_1, Variadic::UnsignedInteger(10u)));
+  EXPECT_CALL(inserter, AddArg(debug_an_1, debug_an_1,
+                               Variadic::UnsignedInteger(10u), _));
 
   EXPECT_CALL(inserter, AddArg(debug_an_2_child_1, debug_an_2_child_1,
-                               Variadic::Boolean(true)));
+                               Variadic::Boolean(true), _));
 
   EXPECT_CALL(inserter, AddArg(debug_an_2_child_2, debug_an_2_child_2_0,
-                               Variadic::String(child21)));
+                               Variadic::String(child21), _));
 
   EXPECT_CALL(inserter, AddArg(debug_an_2_child_2, debug_an_2_child_2_1,
-                               Variadic::Real(2.2)));
+                               Variadic::Real(2.2), _));
 
   EXPECT_CALL(inserter, AddArg(debug_an_2_child_2, debug_an_2_child_2_2,
-                               Variadic::Integer(23)));
+                               Variadic::Integer(23), _));
 
   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(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)));
+              AddArg(debug_an_3, debug_an_3, Variadic::Integer(-3), _));
   EXPECT_CALL(inserter,
-              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)));
+              AddArg(debug_an_4, debug_an_4, Variadic::Boolean(true), _));
   EXPECT_CALL(inserter,
-              AddArg(debug_an_8_foo, debug_an_8_foo, Variadic::Integer(15)));
+              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(debug_an_7, debug_an_7, Variadic::String(val_7), _));
+  EXPECT_CALL(inserter, AddArg(debug_an_8_val8_a, debug_an_8_val8_a,
+                               Variadic::Integer(42), _));
+  EXPECT_CALL(inserter, AddArg(debug_an_8_val8_b, debug_an_8_val8_b,
+                               Variadic::String(val_8b), _));
+  EXPECT_CALL(inserter, AddArg(debug_an_8_arr8, debug_an_8_arr8_0,
+                               Variadic::Integer(1), _));
+  EXPECT_CALL(inserter, AddArg(debug_an_8_arr8, debug_an_8_arr8_1,
+                               Variadic::Integer(2), _));
+  EXPECT_CALL(inserter, AddArg(debug_an_8_arr8, debug_an_8_arr8_2,
+                               Variadic::Integer(3), _));
+  EXPECT_CALL(inserter,
+              AddArg(debug_an_8_foo, debug_an_8_foo, Variadic::Integer(15), _));
 
   context_.sorter->ExtractEventsForced();
 }
 
 TEST_F(ProtoTraceParserTest, TrackEventWithTaskExecution) {
   context_.sorter.reset(new TraceSorter(
-      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+      CreateParser(), std::numeric_limits<int64_t>::max() /*window size*/));
 
   {
     auto* packet = trace_.add_packet();
@@ -1843,14 +1965,12 @@
     thread_desc->set_pid(15);
     thread_desc->set_tid(16);
     thread_desc->set_reference_timestamp_us(1000);
-    thread_desc->set_reference_thread_time_us(2000);
   }
   {
     auto* packet = trace_.add_packet();
     packet->set_trusted_packet_sequence_id(1);
     auto* event = packet->set_track_event();
     event->set_timestamp_delta_us(10);   // absolute: 1010.
-    event->set_thread_time_delta_us(5);  // absolute: 2005.
     event->add_category_iids(1);
     auto* task_execution = event->set_task_execution();
     task_execution->set_posted_from_iid(1);
@@ -1892,16 +2012,16 @@
   MockBoundInserter inserter;
   EXPECT_CALL(*slice_, Begin(1010000, track, cat_1, ev_1, _))
       .WillOnce(DoAll(InvokeArgument<4>(&inserter), Return(1u)));
-  EXPECT_CALL(inserter, AddArg(_, _, Variadic::String(file_1)));
-  EXPECT_CALL(inserter, AddArg(_, _, Variadic::String(func_1)));
-  EXPECT_CALL(inserter, AddArg(_, _, Variadic::UnsignedInteger(42)));
+  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();
 }
 
 TEST_F(ProtoTraceParserTest, TrackEventWithLogMessage) {
   context_.sorter.reset(new TraceSorter(
-      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+      CreateParser(), std::numeric_limits<int64_t>::max() /*window size*/));
 
   {
     auto* packet = trace_.add_packet();
@@ -1911,14 +2031,12 @@
     thread_desc->set_pid(15);
     thread_desc->set_tid(16);
     thread_desc->set_reference_timestamp_us(1000);
-    thread_desc->set_reference_thread_time_us(2000);
   }
   {
     auto* packet = trace_.add_packet();
     packet->set_trusted_packet_sequence_id(1);
     auto* event = packet->set_track_event();
     event->set_timestamp_delta_us(10);   // absolute: 1010.
-    event->set_thread_time_delta_us(5);  // absolute: 2005.
     event->add_category_iids(1);
 
     auto* log_message = event->set_log_message();
@@ -1969,7 +2087,7 @@
       .WillOnce(DoAll(InvokeArgument<5>(&inserter), Return(1u)));
 
   // Call with logMessageBody (body1 in this case).
-  EXPECT_CALL(inserter, AddArg(_, _, Variadic::String(body_1)));
+  EXPECT_CALL(inserter, AddArg(_, _, Variadic::String(body_1), _));
 
   context_.sorter->ExtractEventsForced();
 
@@ -1980,7 +2098,7 @@
 
 TEST_F(ProtoTraceParserTest, TrackEventParseLegacyEventIntoRawTable) {
   context_.sorter.reset(new TraceSorter(
-      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+      CreateParser(), std::numeric_limits<int64_t>::max() /*window size*/));
 
   {
     auto* packet = trace_.add_packet();
@@ -2033,6 +2151,10 @@
   Tokenize();
 
   EXPECT_CALL(*process_, UpdateThread(16, 15)).WillRepeatedly(Return(1));
+  EXPECT_CALL(*event_,
+              PushCounter(1010000, testing::DoubleEq(2005000), TrackId{1}));
+  EXPECT_CALL(*event_,
+              PushCounter(1033000, testing::DoubleEq(2020000), TrackId{1}));
 
   tables::ThreadTable::Row row(16);
   row.upid = 1u;
@@ -2092,7 +2214,7 @@
 
 TEST_F(ProtoTraceParserTest, TrackEventLegacyTimestampsWithClockSnapshot) {
   context_.sorter.reset(new TraceSorter(
-      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+      CreateParser(), std::numeric_limits<int64_t>::max() /*window size*/));
 
   clock_->AddSnapshot(
       {{protos::pbzero::ClockSnapshot::Clock::BOOTTIME, 0},
@@ -2128,16 +2250,17 @@
 
   constexpr TrackId track{0u};
   InSequence in_sequence;  // Below slices should be sorted by timestamp.
+  StringId unknown_cat = storage_->InternString("unknown(1)");
 
   // Timestamp should be adjusted to trace time (BOOTTIME).
-  EXPECT_CALL(*slice_, Begin(10000, track, kNullStringId, kNullStringId, _));
+  EXPECT_CALL(*slice_, Begin(10000, track, unknown_cat, kNullStringId, _));
 
   context_.sorter->ExtractEventsForced();
 }
 
 TEST_F(ProtoTraceParserTest, ParseEventWithClockIdButWithoutClockSnapshot) {
   context_.sorter.reset(new TraceSorter(
-      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+      CreateParser(), std::numeric_limits<int64_t>::max() /*window size*/));
 
   {
     auto* packet = trace_.add_packet();
@@ -2166,7 +2289,7 @@
   static const int kIntValue = 123;
 
   context_.sorter.reset(new TraceSorter(
-      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+      CreateParser(), std::numeric_limits<int64_t>::max() /*window size*/));
 
   {
     auto* packet = trace_.add_packet();
@@ -2205,7 +2328,7 @@
   static const char kFullData[] = "aaabbb";
 
   context_.sorter.reset(new TraceSorter(
-      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+      CreateParser(), std::numeric_limits<int64_t>::max() /*window size*/));
 
   {
     auto* packet = trace_.add_packet();
@@ -2235,7 +2358,7 @@
   static const char kUserTraceEvent[] = "{\"user\":1}";
 
   context_.sorter.reset(new TraceSorter(
-      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+      CreateParser(), std::numeric_limits<int64_t>::max() /*window size*/));
 
   {
     auto* packet = trace_.add_packet();
@@ -2269,7 +2392,7 @@
   static const char kTag2[] = "tag2";
 
   context_.sorter.reset(new TraceSorter(
-      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+      CreateParser(), std::numeric_limits<int64_t>::max() /*window size*/));
 
   auto* metadata = trace_.add_packet()->set_chrome_benchmark_metadata();
   metadata->set_benchmark_name(kName);
@@ -2278,14 +2401,8 @@
 
   Tokenize();
 
-  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]);
-  StringId tags_id = *storage_->string_pool().GetId(
-      metadata::kNames[metadata::benchmark_story_tags]);
+  base::StringView benchmark = metadata::kNames[metadata::benchmark_name];
+  base::StringView tags = metadata::kNames[metadata::benchmark_story_tags];
 
   context_.sorter->ExtractEventsForced();
 
@@ -2293,14 +2410,15 @@
   const auto& meta_values = storage_->metadata_table().str_value();
   EXPECT_EQ(storage_->metadata_table().row_count(), 3u);
 
-  std::vector<std::pair<StringId, StringId>> meta_entries;
+  std::vector<std::pair<base::StringView, base::StringView>> meta_entries;
   for (uint32_t i = 0; i < storage_->metadata_table().row_count(); i++) {
-    meta_entries.emplace_back(std::make_pair(meta_keys[i], meta_values[i]));
+    meta_entries.emplace_back(
+        std::make_pair(meta_keys.GetString(i), meta_values.GetString(i)));
   }
   EXPECT_THAT(meta_entries,
-              UnorderedElementsAreArray({std::make_pair(benchmark_id, name_1),
-                                         std::make_pair(tags_id, tag_1),
-                                         std::make_pair(tags_id, tag_2)}));
+              UnorderedElementsAreArray({std::make_pair(benchmark, kName),
+                                         std::make_pair(tags, kTag1),
+                                         std::make_pair(tags, kTag2)}));
 }
 
 TEST_F(ProtoTraceParserTest, AndroidPackagesList) {
@@ -2337,41 +2455,67 @@
   // 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->arg_table();
-  const auto& metadata = context_.storage->metadata_table();
-
-  Table package_list = metadata.Filter(
-      {metadata.name().eq(metadata::kNames[metadata::android_packages_list])});
+  const auto& package_list = context_.storage->package_list_table();
   ASSERT_EQ(package_list.row_count(), 2u);
 
-  const Column* int_value = package_list.GetColumnByName("int_value");
-  uint32_t first_set_id = static_cast<uint32_t>(int_value->Get(0).long_value);
-  uint32_t second_set_id = static_cast<uint32_t>(int_value->Get(1).long_value);
+  EXPECT_STREQ(storage_->GetString(package_list.package_name()[0]).c_str(),
+               "com.test.app");
+  EXPECT_EQ(package_list.uid()[0], 1000u);
+  EXPECT_EQ(package_list.debuggable()[0], false);
+  EXPECT_EQ(package_list.profileable_from_shell()[0], true);
+  EXPECT_EQ(package_list.version_code()[0], 42);
 
-  // helper to look up arg values
-  auto find_arg = [&args, this](ArgSetId set_id, const char* arg_name) {
-    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");
-  };
+  EXPECT_STREQ(storage_->GetString(package_list.package_name()[1]).c_str(),
+               "com.test.app2");
+  EXPECT_EQ(package_list.uid()[1], 1001u);
+  EXPECT_EQ(package_list.debuggable()[1], false);
+  EXPECT_EQ(package_list.profileable_from_shell()[1], false);
+  EXPECT_EQ(package_list.version_code()[1], 43);
+}
 
-  auto first_name_id = find_arg(first_set_id, "name").string_value;
-  EXPECT_STREQ(storage_->GetString(first_name_id).c_str(), "com.test.app");
-  EXPECT_EQ(find_arg(first_set_id, "uid").uint_value, 1000u);
-  EXPECT_EQ(find_arg(first_set_id, "debuggable").bool_value, false);
-  EXPECT_EQ(find_arg(first_set_id, "profileable_from_shell").bool_value, true);
-  EXPECT_EQ(find_arg(first_set_id, "version_code").int_value, 42);
+TEST_F(ProtoTraceParserTest, AndroidPackagesListDuplicate) {
+  auto* packet = trace_.add_packet();
+  auto* pkg_list = packet->set_packages_list();
 
-  auto second_name_id = find_arg(second_set_id, "name").string_value;
-  EXPECT_STREQ(storage_->GetString(second_name_id).c_str(), "com.test.app2");
-  EXPECT_EQ(find_arg(second_set_id, "uid").uint_value, 1001u);
-  EXPECT_EQ(find_arg(second_set_id, "debuggable").bool_value, false);
-  EXPECT_EQ(find_arg(second_set_id, "profileable_from_shell").bool_value,
-            false);
-  EXPECT_EQ(find_arg(second_set_id, "version_code").int_value, 43);
+  pkg_list->set_read_error(false);
+  pkg_list->set_parse_error(true);
+  {
+    auto* pkg = pkg_list->add_packages();
+    pkg->set_name("com.test.app");
+    pkg->set_uid(1000);
+    pkg->set_debuggable(false);
+    pkg->set_profileable_from_shell(true);
+    pkg->set_version_code(42);
+  }
+  {
+    auto* pkg = pkg_list->add_packages();
+    pkg->set_name("com.test.app");
+    pkg->set_uid(1000);
+    pkg->set_debuggable(false);
+    pkg->set_profileable_from_shell(true);
+    pkg->set_version_code(42);
+  }
+
+  Tokenize();
+
+  // Packet-level errors reflected in stats storage.
+  const auto& stats = context_.storage->stats();
+  EXPECT_FALSE(stats[stats::packages_list_has_read_errors].value);
+  EXPECT_TRUE(stats[stats::packages_list_has_parse_errors].value);
+
+  // Expect two metadata rows, each with an int_value of a separate arg set id.
+  // 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& package_list = context_.storage->package_list_table();
+  ASSERT_EQ(package_list.row_count(), 1u);
+
+  EXPECT_STREQ(storage_->GetString(package_list.package_name()[0]).c_str(),
+               "com.test.app");
+  EXPECT_EQ(package_list.uid()[0], 1000u);
+  EXPECT_EQ(package_list.debuggable()[0], false);
+  EXPECT_EQ(package_list.profileable_from_shell()[0], true);
+  EXPECT_EQ(package_list.version_code()[0], 42);
 }
 
 TEST_F(ProtoTraceParserTest, ParseCPUProfileSamplesIntoTable) {
@@ -2390,6 +2534,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);
@@ -2420,6 +2569,7 @@
 
     samples->add_callstack_iid(1);
     samples->add_timestamp_delta_us(15);
+    samples->set_process_priority(20);
   }
 
   {
@@ -2429,6 +2579,7 @@
 
     samples->add_callstack_iid(42);
     samples->add_timestamp_delta_us(42);
+    samples->set_process_priority(30);
   }
 
   EXPECT_CALL(*process_, UpdateThread(16, 15))
@@ -2441,16 +2592,95 @@
   EXPECT_EQ(samples.row_count(), 3u);
 
   EXPECT_EQ(samples.ts()[0], 11000);
-  EXPECT_EQ(samples.callsite_id()[0], 0);
+  EXPECT_EQ(samples.callsite_id()[0], CallsiteId{0});
   EXPECT_EQ(samples.utid()[0], 1u);
+  EXPECT_EQ(samples.process_priority()[0], 20);
 
   EXPECT_EQ(samples.ts()[1], 26000);
-  EXPECT_EQ(samples.callsite_id()[1], 1);
+  EXPECT_EQ(samples.callsite_id()[1], CallsiteId{1});
   EXPECT_EQ(samples.utid()[1], 1u);
+  EXPECT_EQ(samples.process_priority()[1], 20);
 
   EXPECT_EQ(samples.ts()[2], 68000);
-  EXPECT_EQ(samples.callsite_id()[2], 0);
+  EXPECT_EQ(samples.callsite_id()[2], CallsiteId{0});
   EXPECT_EQ(samples.utid()[2], 1u);
+  EXPECT_EQ(samples.process_priority()[2], 30);
+
+  // Breakpad build_ids should not be modified/mangled.
+  ASSERT_STREQ(
+      context_.storage
+          ->GetString(storage_->stack_profile_mapping_table().build_id()[0])
+          .c_str(),
+      "3BBCFBD372448A727265C3E7C4D954F91");
+}
+
+TEST_F(ProtoTraceParserTest, CPUProfileSamplesTimestampsAreClockMonotonic) {
+  {
+    auto* packet = trace_.add_packet();
+    packet->set_trusted_packet_sequence_id(0);
+
+    // 1000 us monotonic == 10000 us boottime.
+    auto* clock_snapshot = packet->set_clock_snapshot();
+    auto* clock_boot = clock_snapshot->add_clocks();
+    clock_boot->set_clock_id(protos::pbzero::ClockSnapshot::Clock::BOOTTIME);
+    clock_boot->set_timestamp(10000000);
+    auto* clock_monotonic = clock_snapshot->add_clocks();
+    clock_monotonic->set_clock_id(
+        protos::pbzero::ClockSnapshot::Clock::MONOTONIC);
+    clock_monotonic->set_timestamp(1000000);
+  }
+
+  {
+    auto* packet = trace_.add_packet();
+    packet->set_trusted_packet_sequence_id(1);
+    packet->set_incremental_state_cleared(true);
+
+    auto* thread_desc = packet->set_thread_descriptor();
+    thread_desc->set_pid(15);
+    thread_desc->set_tid(16);
+    thread_desc->set_reference_timestamp_us(1000);
+    thread_desc->set_reference_thread_time_us(2000);
+
+    auto* interned_data = packet->set_interned_data();
+
+    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);
+    frame->set_rel_pc(0x42);
+    frame->set_mapping_id(1);
+
+    auto callstack = interned_data->add_callstacks();
+    callstack->set_iid(1);
+    callstack->add_frame_ids(1);
+  }
+
+  {
+    auto* packet = trace_.add_packet();
+    packet->set_trusted_packet_sequence_id(1);
+
+    auto* samples = packet->set_streaming_profile_packet();
+    samples->add_callstack_iid(1);
+    samples->add_timestamp_delta_us(15);
+  }
+
+  EXPECT_CALL(*process_, UpdateThread(16, 15)).WillRepeatedly(Return(1));
+
+  Tokenize();
+
+  const auto& samples = storage_->cpu_profile_stack_sample_table();
+  EXPECT_EQ(samples.row_count(), 1u);
+
+  // Should have been translated to boottime, i.e. 10015 us absolute.
+  EXPECT_EQ(samples.ts()[0], 10015000);
+  EXPECT_EQ(samples.callsite_id()[0], CallsiteId{0});
+  EXPECT_EQ(samples.utid()[0], 1u);
 }
 
 }  // namespace
diff --git a/src/trace_processor/importers/proto/proto_trace_tokenizer.cc b/src/trace_processor/importers/proto/proto_trace_tokenizer.cc
index ca746b7..707f4ca 100644
--- a/src/trace_processor/importers/proto/proto_trace_tokenizer.cc
+++ b/src/trace_processor/importers/proto/proto_trace_tokenizer.cc
@@ -26,14 +26,16 @@
 #include "perfetto/protozero/proto_decoder.h"
 #include "perfetto/protozero/proto_utils.h"
 #include "perfetto/trace_processor/status.h"
-#include "src/trace_processor/clock_tracker.h"
-#include "src/trace_processor/event_tracker.h"
+#include "src/trace_processor/importers/common/clock_tracker.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
 #include "src/trace_processor/importers/ftrace/ftrace_module.h"
+#include "src/trace_processor/importers/gzip/gzip_utils.h"
 #include "src/trace_processor/importers/proto/packet_sequence_state.h"
 #include "src/trace_processor/importers/proto/proto_incremental_state.h"
-#include "src/trace_processor/stats.h"
+#include "src/trace_processor/storage/stats.h"
+#include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/trace_sorter.h"
-#include "src/trace_processor/trace_storage.h"
 
 #include "protos/perfetto/config/trace_config.pbzero.h"
 #include "protos/perfetto/trace/clock_snapshot.pbzero.h"
@@ -41,10 +43,6 @@
 #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 {
 
@@ -56,36 +54,33 @@
 constexpr uint8_t kTracePacketTag =
     MakeTagLengthDelimited(protos::pbzero::Trace::kPacketFieldNumber);
 
-#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
-TraceBlobView Decompress(TraceBlobView input) {
+TraceBlobView Decompress(GzipDecompressor* decompressor, TraceBlobView input) {
+  PERFETTO_DCHECK(gzip::IsGzipSupported());
+
   uint8_t out[4096];
-  std::string s;
 
-  z_stream stream{};
-  stream.next_in = const_cast<uint8_t*>(input.data());
-  stream.avail_in = static_cast<unsigned int>(input.length());
+  std::vector<uint8_t> data;
+  data.reserve(input.length());
 
-  if (inflateInit(&stream) != Z_OK)
-    return TraceBlobView(nullptr, 0, 0);
+  // Ensure that the decompressor is able to cope with a new stream of data.
+  decompressor->Reset();
+  decompressor->SetInput(input.data(), input.length());
 
-  int ret;
-  do {
-    stream.next_out = out;
-    stream.avail_out = sizeof(out);
-    ret = inflate(&stream, Z_NO_FLUSH);
-    if (ret != Z_STREAM_END && ret != Z_OK) {
-      inflateEnd(&stream);
+  using ResultCode = GzipDecompressor::ResultCode;
+  for (auto ret = ResultCode::kOk; ret != ResultCode::kEof;) {
+    auto res = decompressor->Decompress(out, base::ArraySize(out));
+    ret = res.ret;
+    if (ret == ResultCode::kError || ret == ResultCode::kNoProgress ||
+        ret == ResultCode::kNeedsMoreInput)
       return TraceBlobView(nullptr, 0, 0);
-    }
-    s.append(reinterpret_cast<char*>(out), sizeof(out) - stream.avail_out);
-  } while (ret != Z_STREAM_END);
-  inflateEnd(&stream);
 
-  std::unique_ptr<uint8_t[]> output(new uint8_t[s.size()]);
-  memcpy(output.get(), s.data(), s.size());
-  return TraceBlobView(std::move(output), 0, s.size());
+    data.insert(data.end(), out, out + res.bytes_written);
+  }
+
+  std::unique_ptr<uint8_t[]> output(new uint8_t[data.size()]);
+  memcpy(output.get(), data.data(), data.size());
+  return TraceBlobView(std::move(output), 0, data.size());
 }
-#endif  //  PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
 
 }  // namespace
 
@@ -314,11 +309,14 @@
   }
 
   if (decoder.has_compressed_packets()) {
-#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+    if (!gzip::IsGzipSupported())
+      return util::Status("Cannot decode compressed packets. Zlib not enabled");
+
     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);
-    TraceBlobView packets = Decompress(std::move(compressed_packets));
+    TraceBlobView packets =
+        Decompress(&decompressor_, std::move(compressed_packets));
 
     const uint8_t* start = packets.data();
     const uint8_t* end = packets.data() + packets.length();
@@ -338,11 +336,7 @@
       if (PERFETTO_UNLIKELY(!status.ok()))
         return status;
     }
-
     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
@@ -391,6 +385,8 @@
   GetIncrementalStateForPacketSequence(
       packet_decoder.trusted_packet_sequence_id())
       ->OnIncrementalStateCleared();
+  context_->track_tracker->OnIncrementalStateCleared(
+      packet_decoder.trusted_packet_sequence_id());
 }
 
 void ProtoTraceTokenizer::HandlePreviousPacketDropped(
@@ -476,5 +472,7 @@
   return util::OkStatus();
 }
 
+void ProtoTraceTokenizer::NotifyEndOfFile() {}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/proto_trace_tokenizer.h b/src/trace_processor/importers/proto/proto_trace_tokenizer.h
index 0612068..62ddb39 100644
--- a/src/trace_processor/importers/proto/proto_trace_tokenizer.h
+++ b/src/trace_processor/importers/proto/proto_trace_tokenizer.h
@@ -23,6 +23,7 @@
 #include <vector>
 
 #include "src/trace_processor/chunked_trace_reader.h"
+#include "src/trace_processor/importers/gzip/gzip_utils.h"
 #include "src/trace_processor/importers/proto/proto_incremental_state.h"
 #include "src/trace_processor/trace_blob_view.h"
 
@@ -56,6 +57,7 @@
 
   // ChunkedTraceReader implementation.
   util::Status Parse(std::unique_ptr<uint8_t[]>, size_t size) override;
+  void NotifyEndOfFile() override;
 
  private:
   using ConstBytes = protozero::ConstBytes;
@@ -91,6 +93,9 @@
   // Stores incremental state and references to interned data, e.g. for track
   // event protos.
   std::unique_ptr<ProtoIncrementalState> incremental_state;
+
+  // Allows support for compressed trace packets.
+  GzipDecompressor decompressor_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/stack_profile_tracker.cc b/src/trace_processor/importers/proto/stack_profile_tracker.cc
similarity index 87%
rename from src/trace_processor/stack_profile_tracker.cc
rename to src/trace_processor/importers/proto/stack_profile_tracker.cc
index 63443f4..aa996c9 100644
--- a/src/trace_processor/stack_profile_tracker.cc
+++ b/src/trace_processor/importers/proto/stack_profile_tracker.cc
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/stack_profile_tracker.h"
+#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
 
-#include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/string_utils.h"
@@ -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{
@@ -107,7 +116,7 @@
       }
     }
     if (!cur_id) {
-      MappingId mapping_id = mappings->Insert(row);
+      MappingId mapping_id = mappings->Insert(row).id;
       context_->storage->InsertMappingId(row.name, row.build_id, mapping_id);
       cur_id = mapping_id;
     }
@@ -130,15 +139,15 @@
   }
   const StringId& str_id = opt_str_id.value();
 
-  auto maybe_mapping = FindOrInsertMapping(frame.mapping_id, intern_lookup);
-  if (!maybe_mapping) {
+  auto opt_mapping = FindOrInsertMapping(frame.mapping_id, intern_lookup);
+  if (!opt_mapping) {
     context_->storage->IncrementStats(stats::stackprofile_invalid_mapping_id);
     PERFETTO_ELOG("Invalid mapping for frame %" PRIu64, id);
     return base::nullopt;
   }
-  MappingId mapping_id = *maybe_mapping;
+  MappingId mapping_id = *opt_mapping;
 
-  tables::StackProfileFrameTable::Row row{str_id, mapping_id.value,
+  tables::StackProfileFrameTable::Row row{str_id, mapping_id,
                                           static_cast<int64_t>(frame.rel_pc)};
 
   auto* frames = context_->storage->mutable_stack_profile_frame_table();
@@ -162,7 +171,7 @@
       }
     }
     if (!cur_id) {
-      cur_id = frames->Insert(row);
+      cur_id = frames->Insert(row).id;
       context_->storage->InsertFrameRow(
           mapping_id, static_cast<uint64_t>(row.rel_pc), *cur_id);
     }
@@ -176,24 +185,20 @@
     SourceCallstackId id,
     const SourceCallstack& frame_ids,
     const InternLookup* intern_lookup) {
-  // TODO(fmayer): This should be NULL.
+  if (frame_ids.size() == 0)
+    return base::nullopt;
+
   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) {
+  for (uint32_t depth = 0; depth < frame_ids.size(); ++depth) {
+    auto opt_frame_id = FindOrInsertFrame(frame_ids[depth], intern_lookup);
+    if (!opt_frame_id) {
       context_->storage->IncrementStats(stats::stackprofile_invalid_frame_id);
       PERFETTO_ELOG("Unknown frame in callstack; ignoring.");
       return base::nullopt;
     }
-    FrameId frame_id = *maybe_frame_id;
+    FrameId frame_id = *opt_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),
-                                               db_parent_id, frame_id.value};
-
+    tables::StackProfileCallsiteTable::Row row{depth, parent_id, frame_id};
     CallsiteId self_id;
     auto callsite_it = callsite_idx_.find(row);
     if (callsite_it != callsite_idx_.end()) {
@@ -201,11 +206,12 @@
     } else {
       auto* callsite =
           context_->storage->mutable_stack_profile_callsite_table();
-      self_id = callsite->Insert(row);
+      self_id = callsite->Insert(row).id;
       callsite_idx_.emplace(row, self_id);
     }
     parent_id = self_id;
   }
+  PERFETTO_DCHECK(parent_id);  // The loop ran at least once.
   callstack_ids_.emplace(id, *parent_id);
   return parent_id;
 }
diff --git a/src/trace_processor/stack_profile_tracker.h b/src/trace_processor/importers/proto/stack_profile_tracker.h
similarity index 96%
rename from src/trace_processor/stack_profile_tracker.h
rename to src/trace_processor/importers/proto/stack_profile_tracker.h
index 575715c..27ae226 100644
--- a/src/trace_processor/stack_profile_tracker.h
+++ b/src/trace_processor/importers/proto/stack_profile_tracker.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_STACK_PROFILE_TRACKER_H_
-#define SRC_TRACE_PROCESSOR_STACK_PROFILE_TRACKER_H_
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_STACK_PROFILE_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_STACK_PROFILE_TRACKER_H_
 
 #include <deque>
 #include <unordered_map>
@@ -24,7 +24,7 @@
 
 #include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
 #include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/trace_storage.h"
 
 namespace std {
 
@@ -191,6 +191,7 @@
       const InternLookup* intern_lookup);
   base::Optional<FrameId> FindOrInsertFrame(SourceFrameId,
                                             const InternLookup* intern_lookup);
+
   base::Optional<CallsiteId> FindOrInsertCallstack(
       SourceCallstackId,
       const InternLookup* intern_lookup);
@@ -226,4 +227,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_STACK_PROFILE_TRACKER_H_
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_STACK_PROFILE_TRACKER_H_
diff --git a/src/trace_processor/importers/proto/system_probes_module.cc b/src/trace_processor/importers/proto/system_probes_module.cc
index 7d5f254..1a9af08 100644
--- a/src/trace_processor/importers/proto/system_probes_module.cc
+++ b/src/trace_processor/importers/proto/system_probes_module.cc
@@ -32,6 +32,7 @@
   RegisterForField(TracePacket::kProcessStatsFieldNumber, context);
   RegisterForField(TracePacket::kSysStatsFieldNumber, context);
   RegisterForField(TracePacket::kSystemInfoFieldNumber, context);
+  RegisterForField(TracePacket::kCpuInfoFieldNumber, context);
 }
 
 void SystemProbesModule::ParsePacket(const TracePacket::Decoder& decoder,
@@ -50,6 +51,9 @@
     case TracePacket::kSystemInfoFieldNumber:
       parser_.ParseSystemInfo(decoder.system_info());
       return;
+    case TracePacket::kCpuInfoFieldNumber:
+      parser_.ParseCpuInfo(decoder.cpu_info());
+      return;
   }
 }
 
diff --git a/src/trace_processor/importers/proto/system_probes_parser.cc b/src/trace_processor/importers/proto/system_probes_parser.cc
index 5ca7ca1..29050b3 100644
--- a/src/trace_processor/importers/proto/system_probes_parser.cc
+++ b/src/trace_processor/importers/proto/system_probes_parser.cc
@@ -19,17 +19,18 @@
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/traced/sys_stats_counters.h"
 #include "perfetto/protozero/proto_decoder.h"
-#include "src/trace_processor/event_tracker.h"
-#include "src/trace_processor/metadata.h"
-#include "src/trace_processor/metadata_tracker.h"
-#include "src/trace_processor/process_tracker.h"
-#include "src/trace_processor/syscall_tracker.h"
-#include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/proto/metadata_tracker.h"
+#include "src/trace_processor/importers/syscalls/syscall_tracker.h"
+#include "src/trace_processor/storage/metadata.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 #include "protos/perfetto/trace/ps/process_stats.pbzero.h"
 #include "protos/perfetto/trace/ps/process_tree.pbzero.h"
 #include "protos/perfetto/trace/sys_stats/sys_stats.pbzero.h"
 #include "protos/perfetto/trace/system_info.pbzero.h"
+#include "protos/perfetto/trace/system_info/cpu_info.pbzero.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -253,6 +254,17 @@
         pid = fld.as_uint32();
         continue;
       }
+      if (fld.id() ==
+          protos::pbzero::ProcessStats::Process::kThreadsFieldNumber) {
+        if (PERFETTO_UNLIKELY(ms_per_tick_ == 0 ||
+                              thread_time_in_state_cpu_str_ids_.empty())) {
+          context_->storage->IncrementStats(
+              stats::thread_time_in_state_out_of_order);
+          continue;
+        }
+        ParseThreadStats(ts, pid, fld.as_bytes());
+        continue;
+      }
       bool is_counter_field = fld.id() < proc_stats_process_names_.size() &&
                               !proc_stats_process_names_[fld.id()].is_null();
       if (is_counter_field) {
@@ -284,6 +296,35 @@
   }
 }
 
+void SystemProbesParser::ParseThreadStats(int64_t ts,
+                                          uint32_t pid,
+                                          ConstBytes blob) {
+  protos::pbzero::ProcessStats::Thread::Decoder stats(blob.data, blob.size);
+  UniqueTid utid = context_->process_tracker->UpdateThread(
+      static_cast<uint32_t>(stats.tid()), pid);
+  auto index_it = stats.cpu_freq_indices();
+  auto tick_it = stats.cpu_freq_ticks();
+  std::map<StringId, uint64_t> total_ticks_cpu;
+  for (; index_it && tick_it; index_it++, tick_it++) {
+    auto freq_index = *index_it;
+    if (PERFETTO_UNLIKELY(freq_index == 0 ||
+                          freq_index >=
+                              thread_time_in_state_cpu_str_ids_.size())) {
+      context_->storage->IncrementStats(
+          stats::thread_time_in_state_unknown_cpu_freq);
+      continue;
+    }
+    total_ticks_cpu[thread_time_in_state_cpu_str_ids_[freq_index]] += *tick_it;
+  }
+
+  for (auto it : total_ticks_cpu) {
+    TrackId track =
+        context_->track_tracker->InternThreadCounterTrack(it.first, utid);
+    auto ticks = it.second;
+    context_->event_tracker->PushCounter(ts, ticks * ms_per_tick_, track);
+  }
+}
+
 void SystemProbesParser::ParseSystemInfo(ConstBytes blob) {
   protos::pbzero::SystemInfo::Decoder packet(blob.data, blob.size);
   if (packet.has_utsname()) {
@@ -296,6 +337,8 @@
       syscall_tracker->SetArchitecture(kAarch64);
     } else if (machine == "x86_64") {
       syscall_tracker->SetArchitecture(kX86_64);
+    } else if (machine == "i686") {
+      syscall_tracker->SetArchitecture(kX86);
     } else {
       PERFETTO_ELOG("Unknown architecture %s", machine.ToStdString().c_str());
     }
@@ -325,6 +368,28 @@
         Variadic::String(context_->storage->InternString(
             packet.android_build_fingerprint())));
   }
+
+  int64_t hz = packet.hz();
+  if (hz > 0)
+    ms_per_tick_ = 1000u / static_cast<uint64_t>(hz);
+}
+
+void SystemProbesParser::ParseCpuInfo(ConstBytes blob) {
+  protos::pbzero::CpuInfo::Decoder packet(blob.data, blob.size);
+  uint32_t cpu_index = 0;
+  thread_time_in_state_cpu_str_ids_.push_back(
+      context_->storage->InternString("invalid"));
+  for (auto it = packet.cpus(); it; it++) {
+    protos::pbzero::CpuInfo::Cpu::Decoder cpu(*it);
+    std::string cpu_index_string =
+        "cpu" + std::to_string(cpu_index) + ".time_in_state";
+    base::StringView cpu_string(cpu_index_string);
+    for (auto freq_it = cpu.frequencies(); freq_it; freq_it++) {
+      thread_time_in_state_cpu_str_ids_.push_back(
+          context_->storage->InternString(cpu_string));
+    }
+    cpu_index++;
+  }
 }
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/system_probes_parser.h b/src/trace_processor/importers/proto/system_probes_parser.h
index 8dcd810..c9361db 100644
--- a/src/trace_processor/importers/proto/system_probes_parser.h
+++ b/src/trace_processor/importers/proto/system_probes_parser.h
@@ -20,7 +20,7 @@
 #include <vector>
 
 #include "perfetto/protozero/field.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/trace_storage.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -37,8 +37,11 @@
   void ParseProcessStats(int64_t timestamp, ConstBytes);
   void ParseSysStats(int64_t ts, ConstBytes);
   void ParseSystemInfo(ConstBytes);
+  void ParseCpuInfo(ConstBytes);
 
  private:
+  void ParseThreadStats(int64_t timestamp, uint32_t pid, ConstBytes);
+
   TraceProcessorContext* const context_;
 
   const StringId utid_name_id_;
@@ -63,6 +66,11 @@
   // id of ProcessStats::Process.
   static constexpr size_t kProcStatsProcessSize = 11;
   std::array<StringId, kProcStatsProcessSize> proc_stats_process_names_{};
+
+  uint64_t ms_per_tick_ = 0;
+
+  // Maps CPU frequency indices to CPU strings.
+  std::vector<StringId> thread_time_in_state_cpu_str_ids_;
 };
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/track_event.descriptor.h b/src/trace_processor/importers/proto/track_event.descriptor.h
index e0237e6..29cd8ae 100644
--- a/src/trace_processor/importers/proto/track_event.descriptor.h
+++ b/src/trace_processor/importers/proto/track_event.descriptor.h
@@ -25,16 +25,16 @@
 // This file was autogenerated by tools/gen_binary_descriptors. Do not edit.
 
 // SHA1(tools/gen_binary_descriptors)
-// d6628b15181dba5287e35b56b966b39ea93d42b1
+// 3df80477da2ea38cc659967487b37051a154bb69
 // SHA1(protos/perfetto/trace/track_event/track_event.proto)
-// 1cd2627d7a57ddcf4e61b967cd255011bf3a09d5
+// f591683d79fb6881014e72c0a55379bbcfde4b63
 
 // 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, 16107> kTrackEventDescriptor{
+constexpr std::array<uint8_t, 18141> 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,
@@ -1021,6 +1021,142 @@
      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, 0x0a,
+     0xdd, 0x0c, 0x0a, 0x3b, 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, 0x61, 0x74,
+     0x65, 0x6e, 0x63, 0x79, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0x8c, 0x0c, 0x0a,
+     0x11, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x4c, 0x61, 0x74, 0x65, 0x6e,
+     0x63, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x72,
+     0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03,
+     0x52, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x3b, 0x0a,
+     0x04, 0x73, 0x74, 0x65, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32,
+     0x27, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65,
+     0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x2e,
+     0x53, 0x74, 0x65, 0x70, 0x52, 0x04, 0x73, 0x74, 0x65, 0x70, 0x12, 0x2b,
+     0x0a, 0x12, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x72, 0x65, 0x65,
+     0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01,
+     0x28, 0x05, 0x52, 0x0f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x54, 0x72, 0x65,
+     0x65, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x57, 0x0a, 0x0e, 0x63,
+     0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x66,
+     0x6f, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 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, 0x4c, 0x61, 0x74, 0x65,
+     0x6e, 0x63, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x43, 0x6f, 0x6d, 0x70,
+     0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x63,
+     0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f,
+     0x12, 0x21, 0x0a, 0x0c, 0x69, 0x73, 0x5f, 0x63, 0x6f, 0x61, 0x6c, 0x65,
+     0x73, 0x63, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b,
+     0x69, 0x73, 0x43, 0x6f, 0x61, 0x6c, 0x65, 0x73, 0x63, 0x65, 0x64, 0x1a,
+     0x88, 0x01, 0x0a, 0x0d, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e,
+     0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x5e, 0x0a, 0x0e, 0x63, 0x6f, 0x6d,
+     0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18,
+     0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x37, 0x2e, 0x70, 0x65, 0x72, 0x66,
+     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
+     0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63,
+     0x79, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63,
+     0x79, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x54, 0x79,
+     0x70, 0x65, 0x52, 0x0d, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e,
+     0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x69, 0x6d,
+     0x65, 0x5f, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06,
+     0x74, 0x69, 0x6d, 0x65, 0x55, 0x73, 0x22, 0xf2, 0x02, 0x0a, 0x04, 0x53,
+     0x74, 0x65, 0x70, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x54, 0x45, 0x50, 0x5f,
+     0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10,
+     0x00, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x54, 0x45, 0x50, 0x5f, 0x53, 0x45,
+     0x4e, 0x44, 0x5f, 0x49, 0x4e, 0x50, 0x55, 0x54, 0x5f, 0x45, 0x56, 0x45,
+     0x4e, 0x54, 0x5f, 0x55, 0x49, 0x10, 0x03, 0x12, 0x20, 0x0a, 0x1c, 0x53,
+     0x54, 0x45, 0x50, 0x5f, 0x48, 0x41, 0x4e, 0x44, 0x4c, 0x45, 0x5f, 0x49,
+     0x4e, 0x50, 0x55, 0x54, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x49,
+     0x4d, 0x50, 0x4c, 0x10, 0x05, 0x12, 0x28, 0x0a, 0x24, 0x53, 0x54, 0x45,
+     0x50, 0x5f, 0x44, 0x49, 0x44, 0x5f, 0x48, 0x41, 0x4e, 0x44, 0x4c, 0x45,
+     0x5f, 0x49, 0x4e, 0x50, 0x55, 0x54, 0x5f, 0x41, 0x4e, 0x44, 0x5f, 0x4f,
+     0x56, 0x45, 0x52, 0x53, 0x43, 0x52, 0x4f, 0x4c, 0x4c, 0x10, 0x08, 0x12,
+     0x20, 0x0a, 0x1c, 0x53, 0x54, 0x45, 0x50, 0x5f, 0x48, 0x41, 0x4e, 0x44,
+     0x4c, 0x45, 0x5f, 0x49, 0x4e, 0x50, 0x55, 0x54, 0x5f, 0x45, 0x56, 0x45,
+     0x4e, 0x54, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x10, 0x04, 0x12, 0x22, 0x0a,
+     0x1e, 0x53, 0x54, 0x45, 0x50, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x5f, 0x54,
+     0x48, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x53, 0x43, 0x52, 0x4f, 0x4c, 0x4c,
+     0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x02, 0x12, 0x27, 0x0a,
+     0x23, 0x53, 0x54, 0x45, 0x50, 0x5f, 0x48, 0x41, 0x4e, 0x44, 0x4c, 0x45,
+     0x5f, 0x49, 0x4e, 0x50, 0x55, 0x54, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54,
+     0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54,
+     0x10, 0x01, 0x12, 0x29, 0x0a, 0x25, 0x53, 0x54, 0x45, 0x50, 0x5f, 0x48,
+     0x41, 0x4e, 0x44, 0x4c, 0x45, 0x44, 0x5f, 0x49, 0x4e, 0x50, 0x55, 0x54,
+     0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x5f,
+     0x4f, 0x52, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x10, 0x09, 0x12, 0x21, 0x0a,
+     0x1d, 0x53, 0x54, 0x45, 0x50, 0x5f, 0x48, 0x41, 0x4e, 0x44, 0x4c, 0x45,
+     0x44, 0x5f, 0x49, 0x4e, 0x50, 0x55, 0x54, 0x5f, 0x45, 0x56, 0x45, 0x4e,
+     0x54, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x10, 0x0a, 0x12, 0x15, 0x0a, 0x11,
+     0x53, 0x54, 0x45, 0x50, 0x5f, 0x53, 0x57, 0x41, 0x50, 0x5f, 0x42, 0x55,
+     0x46, 0x46, 0x45, 0x52, 0x53, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x53,
+     0x54, 0x45, 0x50, 0x5f, 0x44, 0x52, 0x41, 0x57, 0x5f, 0x41, 0x4e, 0x44,
+     0x5f, 0x53, 0x57, 0x41, 0x50, 0x10, 0x07, 0x22, 0xf5, 0x05, 0x0a, 0x14,
+     0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x6f,
+     0x6e, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x15,
+     0x43, 0x4f, 0x4d, 0x50, 0x4f, 0x4e, 0x45, 0x4e, 0x54, 0x5f, 0x55, 0x4e,
+     0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12,
+     0x2b, 0x0a, 0x27, 0x43, 0x4f, 0x4d, 0x50, 0x4f, 0x4e, 0x45, 0x4e, 0x54,
+     0x5f, 0x49, 0x4e, 0x50, 0x55, 0x54, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54,
+     0x5f, 0x4c, 0x41, 0x54, 0x45, 0x4e, 0x43, 0x59, 0x5f, 0x42, 0x45, 0x47,
+     0x49, 0x4e, 0x5f, 0x52, 0x57, 0x48, 0x10, 0x01, 0x12, 0x38, 0x0a, 0x34,
+     0x43, 0x4f, 0x4d, 0x50, 0x4f, 0x4e, 0x45, 0x4e, 0x54, 0x5f, 0x49, 0x4e,
+     0x50, 0x55, 0x54, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x4c, 0x41,
+     0x54, 0x45, 0x4e, 0x43, 0x59, 0x5f, 0x53, 0x43, 0x52, 0x4f, 0x4c, 0x4c,
+     0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x4f, 0x52, 0x49, 0x47,
+     0x49, 0x4e, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x3e, 0x0a, 0x3a, 0x43, 0x4f,
+     0x4d, 0x50, 0x4f, 0x4e, 0x45, 0x4e, 0x54, 0x5f, 0x49, 0x4e, 0x50, 0x55,
+     0x54, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x4c, 0x41, 0x54, 0x45,
+     0x4e, 0x43, 0x59, 0x5f, 0x46, 0x49, 0x52, 0x53, 0x54, 0x5f, 0x53, 0x43,
+     0x52, 0x4f, 0x4c, 0x4c, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f,
+     0x4f, 0x52, 0x49, 0x47, 0x49, 0x4e, 0x41, 0x4c, 0x10, 0x03, 0x12, 0x2a,
+     0x0a, 0x26, 0x43, 0x4f, 0x4d, 0x50, 0x4f, 0x4e, 0x45, 0x4e, 0x54, 0x5f,
+     0x49, 0x4e, 0x50, 0x55, 0x54, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f,
+     0x4c, 0x41, 0x54, 0x45, 0x4e, 0x43, 0x59, 0x5f, 0x4f, 0x52, 0x49, 0x47,
+     0x49, 0x4e, 0x41, 0x4c, 0x10, 0x04, 0x12, 0x24, 0x0a, 0x20, 0x43, 0x4f,
+     0x4d, 0x50, 0x4f, 0x4e, 0x45, 0x4e, 0x54, 0x5f, 0x49, 0x4e, 0x50, 0x55,
+     0x54, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x4c, 0x41, 0x54, 0x45,
+     0x4e, 0x43, 0x59, 0x5f, 0x55, 0x49, 0x10, 0x05, 0x12, 0x2f, 0x0a, 0x2b,
+     0x43, 0x4f, 0x4d, 0x50, 0x4f, 0x4e, 0x45, 0x4e, 0x54, 0x5f, 0x49, 0x4e,
+     0x50, 0x55, 0x54, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x4c, 0x41,
+     0x54, 0x45, 0x4e, 0x43, 0x59, 0x5f, 0x52, 0x45, 0x4e, 0x44, 0x45, 0x52,
+     0x45, 0x52, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x10, 0x06, 0x12, 0x3a, 0x0a,
+     0x36, 0x43, 0x4f, 0x4d, 0x50, 0x4f, 0x4e, 0x45, 0x4e, 0x54, 0x5f, 0x49,
+     0x4e, 0x50, 0x55, 0x54, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x4c,
+     0x41, 0x54, 0x45, 0x4e, 0x43, 0x59, 0x5f, 0x52, 0x45, 0x4e, 0x44, 0x45,
+     0x52, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c,
+     0x45, 0x44, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x10, 0x07, 0x12, 0x3a, 0x0a,
+     0x36, 0x43, 0x4f, 0x4d, 0x50, 0x4f, 0x4e, 0x45, 0x4e, 0x54, 0x5f, 0x49,
+     0x4e, 0x50, 0x55, 0x54, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x4c,
+     0x41, 0x54, 0x45, 0x4e, 0x43, 0x59, 0x5f, 0x52, 0x45, 0x4e, 0x44, 0x45,
+     0x52, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c,
+     0x45, 0x44, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x10, 0x08, 0x12, 0x3a, 0x0a,
+     0x36, 0x43, 0x4f, 0x4d, 0x50, 0x4f, 0x4e, 0x45, 0x4e, 0x54, 0x5f, 0x49,
+     0x4e, 0x50, 0x55, 0x54, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x4c,
+     0x41, 0x54, 0x45, 0x4e, 0x43, 0x59, 0x5f, 0x53, 0x43, 0x52, 0x4f, 0x4c,
+     0x4c, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x4c, 0x41, 0x53,
+     0x54, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x10, 0x09, 0x12, 0x29, 0x0a,
+     0x25, 0x43, 0x4f, 0x4d, 0x50, 0x4f, 0x4e, 0x45, 0x4e, 0x54, 0x5f, 0x49,
+     0x4e, 0x50, 0x55, 0x54, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x4c,
+     0x41, 0x54, 0x45, 0x4e, 0x43, 0x59, 0x5f, 0x41, 0x43, 0x4b, 0x5f, 0x52,
+     0x57, 0x48, 0x10, 0x0a, 0x12, 0x2f, 0x0a, 0x2b, 0x43, 0x4f, 0x4d, 0x50,
+     0x4f, 0x4e, 0x45, 0x4e, 0x54, 0x5f, 0x49, 0x4e, 0x50, 0x55, 0x54, 0x5f,
+     0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x4c, 0x41, 0x54, 0x45, 0x4e, 0x43,
+     0x59, 0x5f, 0x52, 0x45, 0x4e, 0x44, 0x45, 0x52, 0x45, 0x52, 0x5f, 0x53,
+     0x57, 0x41, 0x50, 0x10, 0x0b, 0x12, 0x2f, 0x0a, 0x2b, 0x43, 0x4f, 0x4d,
+     0x50, 0x4f, 0x4e, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x49, 0x53, 0x50, 0x4c,
+     0x41, 0x59, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x4f,
+     0x52, 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, 0x45, 0x44, 0x5f, 0x46,
+     0x52, 0x41, 0x4d, 0x45, 0x10, 0x0c, 0x12, 0x29, 0x0a, 0x25, 0x43, 0x4f,
+     0x4d, 0x50, 0x4f, 0x4e, 0x45, 0x4e, 0x54, 0x5f, 0x49, 0x4e, 0x50, 0x55,
+     0x54, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x47, 0x50, 0x55, 0x5f,
+     0x53, 0x57, 0x41, 0x50, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x10,
+     0x0d, 0x12, 0x2c, 0x0a, 0x28, 0x43, 0x4f, 0x4d, 0x50, 0x4f, 0x4e, 0x45,
+     0x4e, 0x54, 0x5f, 0x49, 0x4e, 0x50, 0x55, 0x54, 0x5f, 0x45, 0x56, 0x45,
+     0x4e, 0x54, 0x5f, 0x4c, 0x41, 0x54, 0x45, 0x4e, 0x43, 0x59, 0x5f, 0x46,
+     0x52, 0x41, 0x4d, 0x45, 0x5f, 0x53, 0x57, 0x41, 0x50, 0x10, 0x0e, 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,
@@ -1111,273 +1247,306 @@
      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,
+     0x10, 0x25, 0x0a, 0x98, 0x01, 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, 0x4a, 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, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x63, 0x74, 0x69,
+     0x6f, 0x6e, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28,
+     0x04, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x61, 0x73,
+     0x68, 0x0a, 0xed, 0x1a, 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, 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,
-     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, 0x1a, 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, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x1a, 0x33, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70,
+     0x65, 0x6e, 0x74, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x6b, 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, 0x1a, 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, 0x6c, 0x6f, 0x67, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
-     0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 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, 0x1a, 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, 0x1a, 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, 0x1a,
-     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, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x65, 0x64, 0x5f,
-     0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x1a, 0x39, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65,
+     0x74, 0x2f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x61, 0x6e, 0x6e, 0x6f,
+     0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x1a, 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, 0x1a, 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, 0x1a, 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, 0x6c, 0x65, 0x67, 0x61,
-     0x63, 0x79, 0x5f, 0x69, 0x70, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     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, 0x1a, 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, 0x1a, 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, 0x72,
+     0x6f, 0x6d, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x65, 0x64, 0x5f, 0x73, 0x65,
+     0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
+     0x3b, 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, 0x61, 0x74, 0x65, 0x6e, 0x63,
+     0x79, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
      0x1a, 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, 0x22,
-     0xae, 0x12, 0x0a, 0x0a, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x45, 0x76, 0x65,
-     0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f,
-     0x72, 0x79, 0x5f, 0x69, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,
-     0x04, 0x52, 0x0c, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49,
-     0x69, 0x64, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x65, 0x67,
-     0x6f, 0x72, 0x69, 0x65, 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x09, 0x52,
-     0x0a, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x12,
-     0x1b, 0x0a, 0x08, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x69, 0x69, 0x64, 0x18,
-     0x0a, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x07, 0x6e, 0x61, 0x6d,
-     0x65, 0x49, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
-     0x18, 0x17, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x61,
-     0x6d, 0x65, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09,
-     0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
-     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54,
-     0x72, 0x61, 0x63, 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x54, 0x79,
-     0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a,
-     0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x0b,
-     0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x55,
-     0x75, 0x69, 0x64, 0x12, 0x31, 0x0a, 0x14, 0x74, 0x68, 0x72, 0x65, 0x61,
-     0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61,
-     0x5f, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x48, 0x01, 0x52,
-     0x11, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x44,
-     0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12, 0x37, 0x0a, 0x17, 0x74, 0x68,
-     0x72, 0x65, 0x61, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x61, 0x62,
-     0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x5f, 0x75, 0x73, 0x18, 0x11, 0x20,
-     0x01, 0x28, 0x03, 0x48, 0x01, 0x52, 0x14, 0x74, 0x68, 0x72, 0x65, 0x61,
-     0x64, 0x54, 0x69, 0x6d, 0x65, 0x41, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74,
-     0x65, 0x55, 0x73, 0x12, 0x45, 0x0a, 0x1e, 0x74, 0x68, 0x72, 0x65, 0x61,
-     0x64, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f,
-     0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74,
-     0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x48, 0x02, 0x52, 0x1b, 0x74,
-     0x68, 0x72, 0x65, 0x61, 0x64, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63,
-     0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x65, 0x6c,
-     0x74, 0x61, 0x12, 0x4b, 0x0a, 0x21, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64,
-     0x5f, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e,
-     0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x62, 0x73, 0x6f, 0x6c,
-     0x75, 0x74, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x03, 0x48, 0x02, 0x52,
-     0x1e, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x49, 0x6e, 0x73, 0x74, 0x72,
-     0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x41,
-     0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x12, 0x4d, 0x0a, 0x11, 0x64,
-     0x65, 0x62, 0x75, 0x67, 0x5f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74,
-     0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20,
-     0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x41, 0x6e,
-     0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x64, 0x65,
-     0x62, 0x75, 0x67, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f,
-     0x6e, 0x73, 0x12, 0x45, 0x0a, 0x0e, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x65,
-     0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01,
-     0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
-     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x61, 0x73,
-     0x6b, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d,
-     0x74, 0x61, 0x73, 0x6b, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f,
-     0x6e, 0x12, 0x3c, 0x0a, 0x0b, 0x6c, 0x6f, 0x67, 0x5f, 0x6d, 0x65, 0x73,
-     0x73, 0x61, 0x67, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b,
-     0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4c, 0x6f, 0x67, 0x4d, 0x65, 0x73, 0x73,
-     0x61, 0x67, 0x65, 0x52, 0x0a, 0x6c, 0x6f, 0x67, 0x4d, 0x65, 0x73, 0x73,
-     0x61, 0x67, 0x65, 0x12, 0x5d, 0x0a, 0x12, 0x63, 0x63, 0x5f, 0x73, 0x63,
-     0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74,
-     0x65, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x70, 0x65,
+     0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x6c, 0x65, 0x67, 0x61, 0x63,
+     0x79, 0x5f, 0x69, 0x70, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
+     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, 0x22, 0xa6,
+     0x14, 0x0a, 0x0a, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x45, 0x76, 0x65, 0x6e,
+     0x74, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72,
+     0x79, 0x5f, 0x69, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x04,
+     0x52, 0x0c, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x69,
+     0x64, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f,
+     0x72, 0x69, 0x65, 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a,
+     0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x12, 0x1b,
+     0x0a, 0x08, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x69, 0x69, 0x64, 0x18, 0x0a,
+     0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x07, 0x6e, 0x61, 0x6d, 0x65,
+     0x49, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
+     0x17, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d,
+     0x65, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20,
+     0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72,
+     0x61, 0x63, 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x54, 0x79, 0x70,
+     0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74,
+     0x72, 0x61, 0x63, 0x6b, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x0b, 0x20,
+     0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x55, 0x75,
+     0x69, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65,
+     0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28,
+     0x03, 0x52, 0x0c, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x56, 0x61,
+     0x6c, 0x75, 0x65, 0x12, 0x39, 0x0a, 0x19, 0x65, 0x78, 0x74, 0x72, 0x61,
+     0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x74, 0x72, 0x61,
+     0x63, 0x6b, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x73, 0x18, 0x1f, 0x20, 0x03,
+     0x28, 0x04, 0x52, 0x16, 0x65, 0x78, 0x74, 0x72, 0x61, 0x43, 0x6f, 0x75,
+     0x6e, 0x74, 0x65, 0x72, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x55, 0x75, 0x69,
+     0x64, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f,
+     0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75,
+     0x65, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x03, 0x52, 0x12, 0x65, 0x78,
+     0x74, 0x72, 0x61, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x56, 0x61,
+     0x6c, 0x75, 0x65, 0x73, 0x12, 0x4d, 0x0a, 0x11, 0x64, 0x65, 0x62, 0x75,
+     0x67, 0x5f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+     0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 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, 0x52, 0x10, 0x63, 0x63,
-     0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x61,
-     0x74, 0x65, 0x12, 0x4c, 0x0a, 0x11, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65,
-     0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18,
-     0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
-     0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x55, 0x73, 0x65, 0x72, 0x45, 0x76,
-     0x65, 0x6e, 0x74, 0x52, 0x0f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x55,
-     0x73, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x55, 0x0a, 0x14,
-     0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x65, 0x64,
-     0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x1a, 0x20, 0x01,
-     0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
-     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72,
-     0x6f, 0x6d, 0x65, 0x4b, 0x65, 0x79, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76,
-     0x69, 0x63, 0x65, 0x52, 0x12, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x4b,
-     0x65, 0x79, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12,
-     0x4c, 0x0a, 0x11, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x6c, 0x65,
-     0x67, 0x61, 0x63, 0x79, 0x5f, 0x69, 0x70, 0x63, 0x18, 0x1b, 0x20, 0x01,
+     0x73, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x41, 0x6e, 0x6e, 0x6f, 0x74,
+     0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x64, 0x65, 0x62, 0x75, 0x67,
+     0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12,
+     0x45, 0x0a, 0x0e, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x65, 0x78, 0x65, 0x63,
+     0x75, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32,
+     0x1e, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x45, 0x78,
+     0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x74, 0x61, 0x73,
+     0x6b, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3c,
+     0x0a, 0x0b, 0x6c, 0x6f, 0x67, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
+     0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x65,
+     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x73, 0x2e, 0x4c, 0x6f, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
+     0x52, 0x0a, 0x6c, 0x6f, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
+     0x12, 0x5d, 0x0a, 0x12, 0x63, 0x63, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64,
+     0x75, 0x6c, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x18,
+     0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 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, 0x52, 0x10, 0x63, 0x63, 0x53, 0x63, 0x68,
+     0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12,
+     0x4c, 0x0a, 0x11, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x75, 0x73,
+     0x65, 0x72, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x19, 0x20, 0x01,
      0x28, 0x0b, 0x32, 0x20, 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,
-     0x52, 0x0f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x4c, 0x65, 0x67, 0x61,
-     0x63, 0x79, 0x49, 0x70, 0x63, 0x12, 0x5e, 0x0a, 0x17, 0x63, 0x68, 0x72,
-     0x6f, 0x6d, 0x65, 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61,
-     0x6d, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x1c, 0x20, 0x01,
-     0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
-     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72,
-     0x6f, 0x6d, 0x65, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d,
-     0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x15, 0x63, 0x68, 0x72, 0x6f,
-     0x6d, 0x65, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x53,
-     0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x2e, 0x0a, 0x12, 0x74, 0x69, 0x6d,
-     0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61,
-     0x5f, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x48, 0x03, 0x52,
-     0x10, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x44, 0x65,
-     0x6c, 0x74, 0x61, 0x55, 0x73, 0x12, 0x34, 0x0a, 0x15, 0x74, 0x69, 0x6d,
-     0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x61, 0x62, 0x73, 0x6f, 0x6c,
-     0x75, 0x74, 0x65, 0x5f, 0x75, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x03,
-     0x48, 0x03, 0x52, 0x13, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
-     0x70, 0x41, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x55, 0x73, 0x12,
-     0x4a, 0x0a, 0x0c, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x65, 0x76,
-     0x65, 0x6e, 0x74, 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, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x45, 0x76, 0x65,
-     0x6e, 0x74, 0x2e, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x45, 0x76, 0x65,
-     0x6e, 0x74, 0x52, 0x0b, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x45, 0x76,
-     0x65, 0x6e, 0x74, 0x1a, 0xfa, 0x06, 0x0a, 0x0b, 0x4c, 0x65, 0x67, 0x61,
-     0x63, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6e,
-     0x61, 0x6d, 0x65, 0x5f, 0x69, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
-     0x04, 0x52, 0x07, 0x6e, 0x61, 0x6d, 0x65, 0x49, 0x69, 0x64, 0x12, 0x14,
-     0x0a, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
-     0x05, 0x52, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b,
-     0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x73, 0x18,
-     0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x75, 0x72, 0x61, 0x74,
-     0x69, 0x6f, 0x6e, 0x55, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x74, 0x68, 0x72,
-     0x65, 0x61, 0x64, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
-     0x5f, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x74,
-     0x68, 0x72, 0x65, 0x61, 0x64, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
-     0x6e, 0x55, 0x73, 0x12, 0x38, 0x0a, 0x18, 0x74, 0x68, 0x72, 0x65, 0x61,
-     0x64, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f,
-     0x6e, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x0f, 0x20, 0x01, 0x28,
-     0x03, 0x52, 0x16, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x49, 0x6e, 0x73,
-     0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x74,
-     0x61, 0x12, 0x21, 0x0a, 0x0b, 0x75, 0x6e, 0x73, 0x63, 0x6f, 0x70, 0x65,
-     0x64, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00,
-     0x52, 0x0a, 0x75, 0x6e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x49, 0x64,
-     0x12, 0x1b, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x69, 0x64,
-     0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x07, 0x6c, 0x6f,
-     0x63, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x09, 0x67, 0x6c, 0x6f,
-     0x62, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04,
-     0x48, 0x00, 0x52, 0x08, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49, 0x64,
-     0x12, 0x19, 0x0a, 0x08, 0x69, 0x64, 0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65,
-     0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x64, 0x53, 0x63,
-     0x6f, 0x70, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x75, 0x73, 0x65, 0x5f, 0x61,
-     0x73, 0x79, 0x6e, 0x63, 0x5f, 0x74, 0x74, 0x73, 0x18, 0x09, 0x20, 0x01,
-     0x28, 0x08, 0x52, 0x0b, 0x75, 0x73, 0x65, 0x41, 0x73, 0x79, 0x6e, 0x63,
-     0x54, 0x74, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x62, 0x69, 0x6e, 0x64, 0x5f,
-     0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x62, 0x69,
-     0x6e, 0x64, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x62, 0x69, 0x6e, 0x64,
-     0x5f, 0x74, 0x6f, 0x5f, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e,
-     0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x62, 0x69, 0x6e,
-     0x64, 0x54, 0x6f, 0x45, 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67,
-     0x12, 0x5c, 0x0a, 0x0e, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x64, 0x69, 0x72,
-     0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0e,
-     0x32, 0x35, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x6b,
-     0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79,
-     0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x44, 0x69,
-     0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x66, 0x6c, 0x6f,
-     0x77, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x69,
-     0x0a, 0x13, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x5f, 0x65, 0x76,
-     0x65, 0x6e, 0x74, 0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x0e, 0x20,
-     0x01, 0x28, 0x0e, 0x32, 0x39, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
-     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72,
-     0x61, 0x63, 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x65, 0x67,
-     0x61, 0x63, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x49, 0x6e, 0x73,
-     0x74, 0x61, 0x6e, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x63, 0x6f,
-     0x70, 0x65, 0x52, 0x11, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x45,
-     0x76, 0x65, 0x6e, 0x74, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x21, 0x0a,
-     0x0c, 0x70, 0x69, 0x64, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64,
-     0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x70, 0x69, 0x64,
-     0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c,
-     0x74, 0x69, 0x64, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65,
-     0x18, 0x13, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x74, 0x69, 0x64, 0x4f,
-     0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x22, 0x50, 0x0a, 0x0d, 0x46,
-     0x6c, 0x6f, 0x77, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
-     0x12, 0x14, 0x0a, 0x10, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x55, 0x4e, 0x53,
-     0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b,
-     0x0a, 0x07, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x49, 0x4e, 0x10, 0x01, 0x12,
-     0x0c, 0x0a, 0x08, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x4f, 0x55, 0x54, 0x10,
-     0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x49, 0x4e,
-     0x4f, 0x55, 0x54, 0x10, 0x03, 0x22, 0x61, 0x0a, 0x11, 0x49, 0x6e, 0x73,
-     0x74, 0x61, 0x6e, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x63, 0x6f,
-     0x70, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x43, 0x4f, 0x50, 0x45, 0x5f,
-     0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10,
-     0x00, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x43, 0x4f, 0x50, 0x45, 0x5f, 0x47,
-     0x4c, 0x4f, 0x42, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x53,
-     0x43, 0x4f, 0x50, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53,
-     0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x43, 0x4f, 0x50, 0x45, 0x5f,
-     0x54, 0x48, 0x52, 0x45, 0x41, 0x44, 0x10, 0x03, 0x42, 0x04, 0x0a, 0x02,
-     0x69, 0x64, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x22, 0x58, 0x0a, 0x04,
-     0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x59, 0x50, 0x45,
-     0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44,
-     0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53,
-     0x4c, 0x49, 0x43, 0x45, 0x5f, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x10, 0x01,
-     0x12, 0x12, 0x0a, 0x0e, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x4c, 0x49,
-     0x43, 0x45, 0x5f, 0x45, 0x4e, 0x44, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c,
-     0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4e, 0x54,
-     0x10, 0x03, 0x42, 0x0c, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x66,
-     0x69, 0x65, 0x6c, 0x64, 0x42, 0x0d, 0x0a, 0x0b, 0x74, 0x68, 0x72, 0x65,
-     0x61, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x1a, 0x0a, 0x18, 0x74,
+     0x6f, 0x6d, 0x65, 0x55, 0x73, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74,
+     0x52, 0x0f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x55, 0x73, 0x65, 0x72,
+     0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x55, 0x0a, 0x14, 0x63, 0x68, 0x72,
+     0x6f, 0x6d, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x65, 0x64, 0x5f, 0x73, 0x65,
+     0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0b, 0x32,
+     0x23, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65,
+     0x4b, 0x65, 0x79, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+     0x52, 0x12, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x4b, 0x65, 0x79, 0x65,
+     0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4c, 0x0a, 0x11,
+     0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x6c, 0x65, 0x67, 0x61, 0x63,
+     0x79, 0x5f, 0x69, 0x70, 0x63, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x0b, 0x32,
+     0x20, 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, 0x52, 0x0f, 0x63,
+     0x68, 0x72, 0x6f, 0x6d, 0x65, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x49,
+     0x70, 0x63, 0x12, 0x5e, 0x0a, 0x17, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65,
+     0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x5f, 0x73,
+     0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0b, 0x32,
+     0x26, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65,
+     0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x53, 0x61, 0x6d,
+     0x70, 0x6c, 0x65, 0x52, 0x15, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x48,
+     0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x53, 0x61, 0x6d, 0x70,
+     0x6c, 0x65, 0x12, 0x52, 0x0a, 0x13, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65,
+     0x5f, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x69, 0x6e, 0x66,
+     0x6f, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x65,
+     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x4c, 0x61, 0x74, 0x65,
+     0x6e, 0x63, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x11, 0x63, 0x68, 0x72,
+     0x6f, 0x6d, 0x65, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x49, 0x6e,
+     0x66, 0x6f, 0x12, 0x2e, 0x0a, 0x12, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
+     0x61, 0x6d, 0x70, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73,
+     0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x48, 0x01, 0x52, 0x10, 0x74, 0x69,
+     0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x44, 0x65, 0x6c, 0x74, 0x61,
+     0x55, 0x73, 0x12, 0x34, 0x0a, 0x15, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
+     0x61, 0x6d, 0x70, 0x5f, 0x61, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65,
+     0x5f, 0x75, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x03, 0x48, 0x01, 0x52,
+     0x13, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x41, 0x62,
+     0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x55, 0x73, 0x12, 0x31, 0x0a, 0x14,
+     0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f,
+     0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01,
+     0x28, 0x03, 0x48, 0x02, 0x52, 0x11, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64,
+     0x54, 0x69, 0x6d, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x55, 0x73, 0x12,
+     0x37, 0x0a, 0x17, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x74, 0x69,
+     0x6d, 0x65, 0x5f, 0x61, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x5f,
+     0x75, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x03, 0x48, 0x02, 0x52, 0x14,
+     0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x41, 0x62,
+     0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x55, 0x73, 0x12, 0x45, 0x0a, 0x1e,
+     0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x72,
+     0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74,
+     0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03,
+     0x48, 0x03, 0x52, 0x1b, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x49, 0x6e,
+     0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75,
+     0x6e, 0x74, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x4b, 0x0a, 0x21, 0x74,
      0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75,
-     0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42,
+     0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f,
+     0x61, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x18, 0x14, 0x20, 0x01,
+     0x28, 0x03, 0x48, 0x03, 0x52, 0x1e, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64,
+     0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43,
+     0x6f, 0x75, 0x6e, 0x74, 0x41, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65,
+     0x12, 0x4a, 0x0a, 0x0c, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x65,
+     0x76, 0x65, 0x6e, 0x74, 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, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x45, 0x76,
+     0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x45, 0x76,
+     0x65, 0x6e, 0x74, 0x52, 0x0b, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x45,
+     0x76, 0x65, 0x6e, 0x74, 0x1a, 0xfa, 0x06, 0x0a, 0x0b, 0x4c, 0x65, 0x67,
+     0x61, 0x63, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08,
+     0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x69, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
+     0x28, 0x04, 0x52, 0x07, 0x6e, 0x61, 0x6d, 0x65, 0x49, 0x69, 0x64, 0x12,
+     0x14, 0x0a, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01,
+     0x28, 0x05, 0x52, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x12, 0x1f, 0x0a,
+     0x0b, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x73,
+     0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x75, 0x72, 0x61,
+     0x74, 0x69, 0x6f, 0x6e, 0x55, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x74, 0x68,
+     0x72, 0x65, 0x61, 0x64, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
+     0x6e, 0x5f, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10,
+     0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69,
+     0x6f, 0x6e, 0x55, 0x73, 0x12, 0x38, 0x0a, 0x18, 0x74, 0x68, 0x72, 0x65,
+     0x61, 0x64, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69,
+     0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x0f, 0x20, 0x01,
+     0x28, 0x03, 0x52, 0x16, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x49, 0x6e,
+     0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c,
+     0x74, 0x61, 0x12, 0x21, 0x0a, 0x0b, 0x75, 0x6e, 0x73, 0x63, 0x6f, 0x70,
+     0x65, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x48,
+     0x00, 0x52, 0x0a, 0x75, 0x6e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x49,
+     0x64, 0x12, 0x1b, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x69,
+     0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x07, 0x6c,
+     0x6f, 0x63, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x09, 0x67, 0x6c,
+     0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28,
+     0x04, 0x48, 0x00, 0x52, 0x08, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49,
+     0x64, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x64, 0x5f, 0x73, 0x63, 0x6f, 0x70,
+     0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x64, 0x53,
+     0x63, 0x6f, 0x70, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x75, 0x73, 0x65, 0x5f,
+     0x61, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x74, 0x74, 0x73, 0x18, 0x09, 0x20,
+     0x01, 0x28, 0x08, 0x52, 0x0b, 0x75, 0x73, 0x65, 0x41, 0x73, 0x79, 0x6e,
+     0x63, 0x54, 0x74, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x62, 0x69, 0x6e, 0x64,
+     0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x62,
+     0x69, 0x6e, 0x64, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x62, 0x69, 0x6e,
+     0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x69,
+     0x6e, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x62, 0x69,
+     0x6e, 0x64, 0x54, 0x6f, 0x45, 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e,
+     0x67, 0x12, 0x5c, 0x0a, 0x0e, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x64, 0x69,
+     0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28,
+     0x0e, 0x32, 0x35, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63,
+     0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x65, 0x67, 0x61, 0x63,
+     0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x44,
+     0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x66, 0x6c,
+     0x6f, 0x77, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12,
+     0x69, 0x0a, 0x13, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x5f, 0x65,
+     0x76, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x0e,
+     0x20, 0x01, 0x28, 0x0e, 0x32, 0x39, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
+     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54,
+     0x72, 0x61, 0x63, 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x65,
+     0x67, 0x61, 0x63, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x49, 0x6e,
+     0x73, 0x74, 0x61, 0x6e, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x63,
+     0x6f, 0x70, 0x65, 0x52, 0x11, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74,
+     0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x21,
+     0x0a, 0x0c, 0x70, 0x69, 0x64, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69,
+     0x64, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x70, 0x69,
+     0x64, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x21, 0x0a,
+     0x0c, 0x74, 0x69, 0x64, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64,
+     0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x74, 0x69, 0x64,
+     0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x22, 0x50, 0x0a, 0x0d,
+     0x46, 0x6c, 0x6f, 0x77, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f,
+     0x6e, 0x12, 0x14, 0x0a, 0x10, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x55, 0x4e,
+     0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12,
+     0x0b, 0x0a, 0x07, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x49, 0x4e, 0x10, 0x01,
+     0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x4f, 0x55, 0x54,
+     0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x49,
+     0x4e, 0x4f, 0x55, 0x54, 0x10, 0x03, 0x22, 0x61, 0x0a, 0x11, 0x49, 0x6e,
+     0x73, 0x74, 0x61, 0x6e, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x63,
+     0x6f, 0x70, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x43, 0x4f, 0x50, 0x45,
+     0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44,
+     0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x43, 0x4f, 0x50, 0x45, 0x5f,
+     0x47, 0x4c, 0x4f, 0x42, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d,
+     0x53, 0x43, 0x4f, 0x50, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53,
+     0x53, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x43, 0x4f, 0x50, 0x45,
+     0x5f, 0x54, 0x48, 0x52, 0x45, 0x41, 0x44, 0x10, 0x03, 0x42, 0x04, 0x0a,
+     0x02, 0x69, 0x64, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x22, 0x6a, 0x0a,
+     0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x59, 0x50,
+     0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45,
+     0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x59, 0x50, 0x45, 0x5f,
+     0x53, 0x4c, 0x49, 0x43, 0x45, 0x5f, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x10,
+     0x01, 0x12, 0x12, 0x0a, 0x0e, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x4c,
+     0x49, 0x43, 0x45, 0x5f, 0x45, 0x4e, 0x44, 0x10, 0x02, 0x12, 0x10, 0x0a,
+     0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4e,
+     0x54, 0x10, 0x03, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f,
+     0x43, 0x4f, 0x55, 0x4e, 0x54, 0x45, 0x52, 0x10, 0x04, 0x42, 0x0c, 0x0a,
+     0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x42,
      0x0b, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
-     0x22, 0x33, 0x0a, 0x12, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x45, 0x76, 0x65,
-     0x6e, 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1d,
-     0x0a, 0x0a, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x75, 0x75, 0x69, 0x64,
-     0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x72, 0x61, 0x63,
-     0x6b, 0x55, 0x75, 0x69, 0x64, 0x22, 0x35, 0x0a, 0x0d, 0x45, 0x76, 0x65,
-     0x6e, 0x74, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 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,
-     0x22, 0x31, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 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, 0x0d, 0x0a, 0x0b, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x74,
+     0x69, 0x6d, 0x65, 0x42, 0x1a, 0x0a, 0x18, 0x74, 0x68, 0x72, 0x65, 0x61,
+     0x64, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f,
+     0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x6e, 0x0a, 0x12, 0x54,
+     0x72, 0x61, 0x63, 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x66,
+     0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x72, 0x61,
+     0x63, 0x6b, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28,
+     0x04, 0x52, 0x09, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x55, 0x75, 0x69, 0x64,
+     0x12, 0x39, 0x0a, 0x19, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x63, 0x6f,
+     0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f,
+     0x75, 0x75, 0x69, 0x64, 0x73, 0x18, 0x1f, 0x20, 0x03, 0x28, 0x04, 0x52,
+     0x16, 0x65, 0x78, 0x74, 0x72, 0x61, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65,
+     0x72, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x55, 0x75, 0x69, 0x64, 0x73, 0x22,
+     0x35, 0x0a, 0x0d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x43, 0x61, 0x74, 0x65,
+     0x67, 0x6f, 0x72, 0x79, 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, 0x22, 0x31, 0x0a, 0x09, 0x45, 0x76,
+     0x65, 0x6e, 0x74, 0x4e, 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}};
 
 }  // 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 1bbbebd..a2fcf28 100644
--- a/src/trace_processor/importers/proto/track_event_module.cc
+++ b/src/trace_processor/importers/proto/track_event_module.cc
@@ -17,9 +17,9 @@
 
 #include "perfetto/base/build_config.h"
 #include "perfetto/ext/base/string_utils.h"
+#include "src/trace_processor/importers/common/track_tracker.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 "src/trace_processor/types/trace_processor_context.h"
 
 #include "protos/perfetto/config/data_source_config.pbzero.h"
 #include "protos/perfetto/config/trace_config.pbzero.h"
@@ -71,10 +71,8 @@
       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());
+      parser_.ParseTrackEvent(ttp.timestamp, ttp.track_event_data.get(),
+                              decoder.track_event());
       break;
     case TracePacket::kProcessDescriptorFieldNumber:
       // TODO(eseckler): Remove once Chrome has switched to TrackDescriptors.
diff --git a/src/trace_processor/importers/proto/track_event_parser.cc b/src/trace_processor/importers/proto/track_event_parser.cc
index 2ca1a28..d928a00 100644
--- a/src/trace_processor/importers/proto/track_event_parser.cc
+++ b/src/trace_processor/importers/proto/track_event_parser.cc
@@ -19,21 +19,28 @@
 #include <string>
 
 #include "perfetto/base/logging.h"
-#include "src/trace_processor/args_tracker.h"
+#include "perfetto/ext/base/string_writer.h"
+#include "perfetto/trace_processor/status.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/importers/json/json_utils.h"
 #include "src/trace_processor/importers/proto/args_table_utils.h"
-#include "src/trace_processor/importers/proto/chrome_compositor_scheduler_state.descriptor.h"
 #include "src/trace_processor/importers/proto/packet_sequence_state.h"
-#include "src/trace_processor/process_tracker.h"
-#include "src/trace_processor/track_tracker.h"
+#include "src/trace_processor/importers/proto/track_event.descriptor.h"
+#include "src/trace_processor/util/status_macros.h"
 
 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
 #include "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.pbzero.h"
 #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_latency_info.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/counter_descriptor.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"
@@ -47,6 +54,9 @@
 namespace trace_processor {
 
 namespace {
+using BoundInserter = ArgsTracker::BoundInserter;
+using protos::pbzero::TrackEvent;
+using LegacyEvent = TrackEvent::LegacyEvent;
 using protozero::ConstBytes;
 
 // Slices which have been opened but haven't been closed yet will be marked
@@ -57,7 +67,7 @@
 void AddStringToArgsTable(const char* field,
                           const protozero::ConstChars& str,
                           const ProtoToArgsTable::ParsingOverrideState& state,
-                          ArgsTracker::BoundInserter* inserter) {
+                          BoundInserter* inserter) {
   auto val = state.context->storage->InternString(base::StringView(str));
   auto key = state.context->storage->InternString(base::StringView(field));
   inserter->AddArg(key, Variadic::String(val));
@@ -67,7 +77,7 @@
     std::string prefix,
     const ProtoToArgsTable::ParsingOverrideState& state,
     const protozero::Field& field,
-    ArgsTracker::BoundInserter* inserter) {
+    BoundInserter* inserter) {
   auto* decoder = state.sequence_state->LookupInternedMessage<
       protos::pbzero::InternedData::kSourceLocationsFieldNumber,
       protos::pbzero::SourceLocation>(field.as_uint64());
@@ -102,8 +112,1087 @@
 }
 }  // namespace
 
+class TrackEventParser::EventImporter {
+ public:
+  EventImporter(TrackEventParser* parser,
+                int64_t ts,
+                TrackEventData* event_data,
+                ConstBytes blob)
+      : context_(parser->context_),
+        storage_(context_->storage.get()),
+        parser_(parser),
+        ts_(ts),
+        event_data_(event_data),
+        sequence_state_(event_data_->sequence_state),
+        blob_(std::move(blob)),
+        event_(blob_),
+        legacy_event_(event_.legacy_event()),
+        defaults_(sequence_state_->GetTrackEventDefaults()) {}
+
+  util::Status Import() {
+    // TODO(eseckler): This legacy event field will eventually be replaced by
+    // fields in TrackEvent itself.
+    if (PERFETTO_UNLIKELY(!event_.type() && !legacy_event_.has_phase()))
+      return util::ErrStatus("TrackEvent without type or phase");
+
+    category_id_ = ParseTrackEventCategory();
+    name_id_ = ParseTrackEventName();
+
+    RETURN_IF_ERROR(ParseTrackAssociation());
+
+    // Counter-type events don't support arguments (those are on the
+    // CounterDescriptor instead). All they have is a |counter_value|.
+    if (event_.type() == TrackEvent::TYPE_COUNTER) {
+      ParseCounterEvent();
+      return util::OkStatus();
+    }
+
+    // If we have legacy thread time / instruction count fields, also parse them
+    // into the counters tables.
+    ParseLegacyThreadTimeAndInstructionsAsCounters();
+
+    // Parse extra counter values before parsing the actual event. This way, we
+    // can update the slice's thread time / instruction count fields based on
+    // these counter values and also parse them as slice attributes / arguments.
+    ParseExtraCounterValues();
+
+    // TODO(eseckler): Replace phase with type and remove handling of
+    // legacy_event_.phase() once it is no longer used by producers.
+    int32_t phase = ParsePhaseOrType();
+
+    switch (static_cast<char>(phase)) {
+      case 'B':  // TRACE_EVENT_PHASE_BEGIN.
+        return ParseThreadBeginEvent();
+      case 'E':  // TRACE_EVENT_PHASE_END.
+        return ParseThreadEndEvent();
+      case 'X':  // TRACE_EVENT_PHASE_COMPLETE.
+        return ParseThreadCompleteEvent();
+      case 'i':
+      case 'I':  // TRACE_EVENT_PHASE_INSTANT.
+        return ParseThreadInstantEvent();
+      case 'b':  // TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN
+        return ParseAsyncBeginEvent();
+      case 'e':  // TRACE_EVENT_PHASE_NESTABLE_ASYNC_END
+        return ParseAsyncEndEvent();
+      case 'n':  // TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT
+        return ParseAsyncInstantEvent();
+      case 'M':  // TRACE_EVENT_PHASE_METADATA (process and thread names).
+        return ParseMetadataEvent();
+      default:
+        // Other events are proxied via the raw table for JSON export.
+        return ParseLegacyEventAsRawEvent();
+    }
+  }
+
+ private:
+  StringId ParseTrackEventCategory() {
+    StringId category_id = kNullStringId;
+
+    std::vector<uint64_t> category_iids;
+    for (auto it = event_.category_iids(); it; ++it) {
+      category_iids.push_back(*it);
+    }
+    std::vector<protozero::ConstChars> category_strings;
+    for (auto it = event_.categories(); it; ++it) {
+      category_strings.push_back(*it);
+    }
+
+    // If there's a single category, we can avoid building a concatenated
+    // string.
+    if (PERFETTO_LIKELY(category_iids.size() == 1 &&
+                        category_strings.empty())) {
+      auto* decoder = sequence_state_->LookupInternedMessage<
+          protos::pbzero::InternedData::kEventCategoriesFieldNumber,
+          protos::pbzero::EventCategory>(category_iids[0]);
+      if (decoder) {
+        category_id = storage_->InternString(decoder->name());
+      } else {
+        char buffer[32];
+        base::StringWriter writer(buffer, sizeof(buffer));
+        writer.AppendLiteral("unknown(");
+        writer.AppendUnsignedInt(category_iids[0]);
+        writer.AppendChar(')');
+        category_id = storage_->InternString(writer.GetStringView());
+      }
+    } else if (category_iids.empty() && category_strings.size() == 1) {
+      category_id = storage_->InternString(category_strings[0]);
+    } else if (category_iids.size() + category_strings.size() > 1) {
+      // We concatenate the category strings together since we currently only
+      // support a single "cat" column.
+      // TODO(eseckler): Support multi-category events in the table schema.
+      std::string categories;
+      for (uint64_t iid : category_iids) {
+        auto* decoder = sequence_state_->LookupInternedMessage<
+            protos::pbzero::InternedData::kEventCategoriesFieldNumber,
+            protos::pbzero::EventCategory>(iid);
+        if (!decoder)
+          continue;
+        base::StringView name = decoder->name();
+        if (!categories.empty())
+          categories.append(",");
+        categories.append(name.data(), name.size());
+      }
+      for (const protozero::ConstChars& cat : category_strings) {
+        if (!categories.empty())
+          categories.append(",");
+        categories.append(cat.data, cat.size);
+      }
+      if (!categories.empty())
+        category_id = storage_->InternString(base::StringView(categories));
+    }
+
+    return category_id;
+  }
+
+  StringId ParseTrackEventName() {
+    uint64_t name_iid = event_.name_iid();
+    if (!name_iid)
+      name_iid = legacy_event_.name_iid();
+
+    if (PERFETTO_LIKELY(name_iid)) {
+      auto* decoder = sequence_state_->LookupInternedMessage<
+          protos::pbzero::InternedData::kEventNamesFieldNumber,
+          protos::pbzero::EventName>(name_iid);
+      if (decoder)
+        return storage_->InternString(decoder->name());
+    } else if (event_.has_name()) {
+      return storage_->InternString(event_.name());
+    }
+
+    return kNullStringId;
+  }
+
+  util::Status ParseTrackAssociation() {
+    TrackTracker* track_tracker = context_->track_tracker.get();
+    ProcessTracker* procs = context_->process_tracker.get();
+
+    // Consider track_uuid from the packet and TrackEventDefaults, fall back to
+    // the default descriptor track (uuid 0).
+    track_uuid_ = event_.has_track_uuid()
+                      ? event_.track_uuid()
+                      : (defaults_ && defaults_->has_track_uuid()
+                             ? defaults_->track_uuid()
+                             : 0u);
+
+    // Determine track from track_uuid specified in either TrackEvent or
+    // 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_);
+      if (!opt_track_id) {
+        track_tracker->ReserveDescriptorChildTrack(track_uuid_,
+                                                   /*parent_uuid=*/0);
+        opt_track_id = track_tracker->GetDescriptorTrack(track_uuid_);
+      }
+      track_id_ = *opt_track_id;
+
+      auto thread_track_row =
+          storage_->thread_track_table().id().IndexOf(track_id_);
+      if (thread_track_row) {
+        utid_ = storage_->thread_track_table().utid()[*thread_track_row];
+        upid_ = storage_->thread_table().upid()[*utid_];
+      } else {
+        auto process_track_row =
+            storage_->process_track_table().id().IndexOf(track_id_);
+        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 {
+          auto* tracks = context_->storage->mutable_track_table();
+          auto track_index = tracks->id().IndexOf(track_id_);
+          if (track_index) {
+            const StringPool::Id& id = tracks->name()[*track_index];
+            if (id.is_null())
+              tracks->mutable_name()->Set(*track_index, name_id_);
+          }
+
+          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());
+            legacy_passthrough_utid_ = procs->UpdateThread(tid, pid);
+          }
+        }
+      }
+    } else {
+      bool pid_tid_state_valid = sequence_state_->state()->pid_and_tid_valid();
+
+      // We have a 0-value |track_uuid|. Nevertheless, we should only fall back
+      // if we have either no |track_uuid| specified at all or |track_uuid| was
+      // set explicitly to 0 (e.g. to override a default track_uuid) and we have
+      // a legacy phase. Events with real phases should use |track_uuid| to
+      // specify a different track (or use the pid/tid_override fields).
+      bool fallback_to_legacy_pid_tid_tracks =
+          (!event_.has_track_uuid() || !event_.has_type()) &&
+          pid_tid_state_valid;
+
+      // Always allow fallback if we have a process override.
+      fallback_to_legacy_pid_tid_tracks |= legacy_event_.has_pid_override();
+
+      // A thread override requires a valid pid.
+      fallback_to_legacy_pid_tid_tracks |=
+          legacy_event_.has_tid_override() && pid_tid_state_valid;
+
+      if (fallback_to_legacy_pid_tid_tracks) {
+        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()) {
+          pid = static_cast<uint32_t>(legacy_event_.pid_override());
+          tid = static_cast<uint32_t>(-1);
+        }
+        if (legacy_event_.has_tid_override())
+          tid = static_cast<uint32_t>(legacy_event_.tid_override());
+
+        utid_ = procs->UpdateThread(tid, pid);
+        upid_ = storage_->thread_table().upid()[*utid_];
+        track_id_ = track_tracker->InternThreadTrack(*utid_);
+      } else {
+        track_id_ = track_tracker->GetOrCreateDefaultDescriptorTrack();
+      }
+    }
+
+    if (!legacy_event_.has_phase())
+      return util::OkStatus();
+
+    // Legacy phases may imply a different track than the one specified by
+    // the fallback (or default track uuid) above.
+    switch (legacy_event_.phase()) {
+      case 'b':
+      case 'e':
+      case 'n': {
+        // Intern tracks for legacy async events based on legacy event ids.
+        int64_t source_id = 0;
+        bool source_id_is_process_scoped = false;
+        if (legacy_event_.has_unscoped_id()) {
+          source_id = static_cast<int64_t>(legacy_event_.unscoped_id());
+        } else if (legacy_event_.has_global_id()) {
+          source_id = static_cast<int64_t>(legacy_event_.global_id());
+        } else if (legacy_event_.has_local_id()) {
+          if (!upid_) {
+            return util::ErrStatus(
+                "TrackEvent with local_id without process association");
+          }
+
+          source_id = static_cast<int64_t>(legacy_event_.local_id());
+          source_id_is_process_scoped = true;
+        } else {
+          return util::ErrStatus("Async LegacyEvent without ID");
+        }
+
+        // Catapult treats nestable async events of different categories with
+        // the same ID as separate tracks. We replicate the same behavior
+        // here.
+        StringId id_scope = category_id_;
+        if (legacy_event_.has_id_scope()) {
+          std::string concat = storage_->GetString(category_id_).ToStdString() +
+                               ":" + legacy_event_.id_scope().ToStdString();
+          id_scope = storage_->InternString(base::StringView(concat));
+        }
+
+        track_id_ = context_->track_tracker->InternLegacyChromeAsyncTrack(
+            name_id_, upid_ ? *upid_ : 0, source_id,
+            source_id_is_process_scoped, id_scope);
+        legacy_passthrough_utid_ = utid_;
+        break;
+      }
+      case 'i':
+      case 'I': {
+        // Intern tracks for global or process-scoped legacy instant events.
+        switch (legacy_event_.instant_event_scope()) {
+          case LegacyEvent::SCOPE_UNSPECIFIED:
+          case LegacyEvent::SCOPE_THREAD:
+            // Thread-scoped legacy instant events already have the right
+            // track based on the tid/pid of the sequence.
+            if (!utid_) {
+              return util::ErrStatus(
+                  "Thread-scoped instant event without thread association");
+            }
+            break;
+          case LegacyEvent::SCOPE_GLOBAL:
+            track_id_ = context_->track_tracker
+                            ->GetOrCreateLegacyChromeGlobalInstantTrack();
+            legacy_passthrough_utid_ = utid_;
+            utid_ = base::nullopt;
+            break;
+          case LegacyEvent::SCOPE_PROCESS:
+            if (!upid_) {
+              return util::ErrStatus(
+                  "Process-scoped instant event without process association");
+            }
+
+            track_id_ =
+                context_->track_tracker->InternLegacyChromeProcessInstantTrack(
+                    *upid_);
+            legacy_passthrough_utid_ = utid_;
+            utid_ = base::nullopt;
+            break;
+        }
+        break;
+      }
+      default:
+        break;
+    }
+
+    return util::OkStatus();
+  }
+
+  int32_t ParsePhaseOrType() {
+    if (legacy_event_.has_phase())
+      return legacy_event_.phase();
+
+    switch (event_.type()) {
+      case TrackEvent::TYPE_SLICE_BEGIN:
+        return utid_ ? 'B' : 'b';
+      case TrackEvent::TYPE_SLICE_END:
+        return utid_ ? 'E' : 'e';
+      case TrackEvent::TYPE_INSTANT:
+        return utid_ ? 'i' : 'n';
+      default:
+        PERFETTO_FATAL("unexpected event type %d", event_.type());
+        return 0;
+    }
+  }
+
+  void ParseCounterEvent() {
+    // Tokenizer ensures that TYPE_COUNTER events are associated with counter
+    // tracks and have values.
+    PERFETTO_DCHECK(storage_->counter_track_table().id().IndexOf(track_id_));
+    PERFETTO_DCHECK(event_.has_counter_value());
+
+    context_->event_tracker->PushCounter(ts_, event_data_->counter_value,
+                                         track_id_);
+  }
+
+  void ParseLegacyThreadTimeAndInstructionsAsCounters() {
+    if (!utid_)
+      return;
+    // When these fields are set, we don't expect TrackDescriptor-based counters
+    // for thread time or instruction count for this thread in the trace, so we
+    // intern separate counter tracks based on name + utid.
+    if (event_data_->thread_timestamp) {
+      TrackId track_id = context_->track_tracker->InternThreadCounterTrack(
+          parser_->counter_name_thread_time_id_, *utid_);
+      context_->event_tracker->PushCounter(ts_, event_data_->thread_timestamp,
+                                           track_id);
+      if (legacy_event_.duration_us() &&
+          legacy_event_.has_thread_duration_us()) {
+        context_->event_tracker->PushCounter(
+            ts_ + (legacy_event_.duration_us() * 1000),
+            event_data_->thread_timestamp +
+                (legacy_event_.thread_duration_us() * 1000),
+            track_id);
+      }
+    }
+    if (event_data_->thread_instruction_count) {
+      TrackId track_id = context_->track_tracker->InternThreadCounterTrack(
+          parser_->counter_name_thread_instruction_count_id_, *utid_);
+      context_->event_tracker->PushCounter(
+          ts_, event_data_->thread_instruction_count, track_id);
+      if (legacy_event_.duration_us() &&
+          legacy_event_.has_thread_instruction_delta()) {
+        context_->event_tracker->PushCounter(
+            ts_ + (legacy_event_.duration_us() * 1000),
+            event_data_->thread_instruction_count +
+                legacy_event_.thread_instruction_delta(),
+            track_id);
+      }
+    }
+  }
+
+  void ParseExtraCounterValues() {
+    if (!event_.has_extra_counter_values())
+      return;
+
+    protozero::RepeatedFieldIterator<uint64_t> track_uuid_it;
+    if (event_.has_extra_counter_track_uuids()) {
+      track_uuid_it = event_.extra_counter_track_uuids();
+    } else if (defaults_->has_extra_counter_track_uuids()) {
+      track_uuid_it = defaults_->extra_counter_track_uuids();
+    }
+
+    size_t index = 0;
+    for (auto value_it = event_.extra_counter_values(); value_it;
+         ++value_it, ++track_uuid_it, ++index) {
+      // Tokenizer ensures that there aren't more values than uuids, that we
+      // don't have more values than kMaxNumExtraCounters and that the
+      // track_uuids are for valid counter tracks.
+      PERFETTO_DCHECK(track_uuid_it);
+      PERFETTO_DCHECK(index < TrackEventData::kMaxNumExtraCounters);
+
+      base::Optional<TrackId> track_id =
+          context_->track_tracker->GetDescriptorTrack(*track_uuid_it);
+      base::Optional<uint32_t> counter_row =
+          storage_->counter_track_table().id().IndexOf(*track_id);
+
+      int64_t value = event_data_->extra_counter_values[index];
+      context_->event_tracker->PushCounter(ts_, value, *track_id);
+
+      // Also import thread_time and thread_instruction_count counters into
+      // slice columns to simplify JSON export.
+      StringId counter_name =
+          storage_->counter_track_table().name()[*counter_row];
+      if (counter_name == parser_->counter_name_thread_time_id_) {
+        event_data_->thread_timestamp = value;
+      } else if (counter_name ==
+                 parser_->counter_name_thread_instruction_count_id_) {
+        event_data_->thread_instruction_count = value;
+      }
+    }
+  }
+
+  util::Status ParseThreadBeginEvent() {
+    if (!utid_) {
+      return util::ErrStatus(
+          "TrackEvent with phase B without thread association");
+    }
+
+    auto opt_slice_id = context_->slice_tracker->Begin(
+        ts_, track_id_, category_id_, name_id_,
+        [this](BoundInserter* inserter) { ParseTrackEventArgs(inserter); });
+    if (opt_slice_id.has_value()) {
+      auto* thread_slices = storage_->mutable_thread_slices();
+      PERFETTO_DCHECK(!thread_slices->slice_count() ||
+                      thread_slices->slice_ids().back() < opt_slice_id.value());
+      thread_slices->AddThreadSlice(
+          opt_slice_id.value(), event_data_->thread_timestamp,
+          kPendingThreadDuration, event_data_->thread_instruction_count,
+          kPendingThreadInstructionDelta);
+    }
+
+    return util::OkStatus();
+  }
+
+  util::Status ParseThreadEndEvent() {
+    if (!utid_) {
+      return util::ErrStatus(
+          "TrackEvent with phase E without thread association");
+    }
+
+    auto opt_slice_id = context_->slice_tracker->End(
+        ts_, track_id_, category_id_, name_id_,
+        [this](BoundInserter* inserter) { ParseTrackEventArgs(inserter); });
+    if (opt_slice_id.has_value()) {
+      auto* thread_slices = storage_->mutable_thread_slices();
+      thread_slices->UpdateThreadDeltasForSliceId(
+          opt_slice_id.value(), event_data_->thread_timestamp,
+          event_data_->thread_instruction_count);
+    }
+
+    return util::OkStatus();
+  }
+
+  util::Status ParseThreadCompleteEvent() {
+    if (!utid_) {
+      return util::ErrStatus(
+          "TrackEvent with phase X without thread association");
+    }
+
+    auto duration_ns = legacy_event_.duration_us() * 1000;
+    if (duration_ns < 0)
+      return util::ErrStatus("TrackEvent with phase X with negative duration");
+    auto opt_slice_id = context_->slice_tracker->Scoped(
+        ts_, track_id_, category_id_, name_id_, duration_ns,
+        [this](BoundInserter* inserter) { ParseTrackEventArgs(inserter); });
+    if (opt_slice_id.has_value()) {
+      auto* thread_slices = storage_->mutable_thread_slices();
+      PERFETTO_DCHECK(!thread_slices->slice_count() ||
+                      thread_slices->slice_ids().back() < opt_slice_id.value());
+      auto thread_duration_ns = legacy_event_.thread_duration_us() * 1000;
+      thread_slices->AddThreadSlice(
+          opt_slice_id.value(), event_data_->thread_timestamp,
+          thread_duration_ns, event_data_->thread_instruction_count,
+          legacy_event_.thread_instruction_delta());
+    }
+
+    return util::OkStatus();
+  }
+
+  util::Status ParseThreadInstantEvent() {
+    // Handle instant events as slices with zero duration, so that they end
+    // up nested underneath their parent slices.
+    int64_t duration_ns = 0;
+    int64_t tidelta = 0;
+    auto opt_slice_id = context_->slice_tracker->Scoped(
+        ts_, track_id_, category_id_, name_id_, duration_ns,
+        [this](BoundInserter* inserter) { ParseTrackEventArgs(inserter); });
+    if (utid_ && opt_slice_id.has_value()) {
+      auto* thread_slices = storage_->mutable_thread_slices();
+      PERFETTO_DCHECK(!thread_slices->slice_count() ||
+                      thread_slices->slice_ids().back() < opt_slice_id.value());
+      thread_slices->AddThreadSlice(
+          opt_slice_id.value(), event_data_->thread_timestamp, duration_ns,
+          event_data_->thread_instruction_count, tidelta);
+    }
+    return util::OkStatus();
+  }
+
+  util::Status ParseAsyncBeginEvent() {
+    auto opt_slice_id = context_->slice_tracker->Begin(
+        ts_, track_id_, category_id_, name_id_,
+        [this](BoundInserter* inserter) { ParseTrackEventArgs(inserter); });
+    // For the time beeing, we only create vtrack slice rows if we need to
+    // store thread timestamps/counters.
+    if (legacy_event_.use_async_tts() && opt_slice_id.has_value()) {
+      auto* vtrack_slices = storage_->mutable_virtual_track_slices();
+      PERFETTO_DCHECK(!vtrack_slices->slice_count() ||
+                      vtrack_slices->slice_ids().back() < opt_slice_id.value());
+      vtrack_slices->AddVirtualTrackSlice(
+          opt_slice_id.value(), event_data_->thread_timestamp,
+          kPendingThreadDuration, event_data_->thread_instruction_count,
+          kPendingThreadInstructionDelta);
+    }
+    return util::OkStatus();
+  }
+
+  util::Status ParseAsyncEndEvent() {
+    auto opt_slice_id = context_->slice_tracker->End(
+        ts_, track_id_, category_id_, name_id_,
+        [this](BoundInserter* inserter) { ParseTrackEventArgs(inserter); });
+    if (legacy_event_.use_async_tts() && opt_slice_id.has_value()) {
+      auto* vtrack_slices = storage_->mutable_virtual_track_slices();
+      vtrack_slices->UpdateThreadDeltasForSliceId(
+          opt_slice_id.value(), event_data_->thread_timestamp,
+          event_data_->thread_instruction_count);
+    }
+    return util::OkStatus();
+  }
+
+  util::Status ParseAsyncInstantEvent() {
+    // Handle instant events as slices with zero duration, so that they end
+    // up nested underneath their parent slices.
+    int64_t duration_ns = 0;
+    int64_t tidelta = 0;
+    auto opt_slice_id = context_->slice_tracker->Scoped(
+        ts_, track_id_, category_id_, name_id_, duration_ns,
+        [this](BoundInserter* inserter) { ParseTrackEventArgs(inserter); });
+    if (legacy_event_.use_async_tts() && opt_slice_id.has_value()) {
+      auto* vtrack_slices = storage_->mutable_virtual_track_slices();
+      PERFETTO_DCHECK(!vtrack_slices->slice_count() ||
+                      vtrack_slices->slice_ids().back() < opt_slice_id.value());
+      vtrack_slices->AddVirtualTrackSlice(
+          opt_slice_id.value(), event_data_->thread_timestamp, duration_ns,
+          event_data_->thread_instruction_count, tidelta);
+    }
+    return util::OkStatus();
+  }
+
+  util::Status ParseMetadataEvent() {
+    ProcessTracker* procs = context_->process_tracker.get();
+
+    // Parse process and thread names from correspondingly named events.
+    NullTermStringView event_name = storage_->GetString(name_id_);
+    PERFETTO_DCHECK(event_name.data());
+    if (strcmp(event_name.c_str(), "thread_name") == 0) {
+      if (!utid_) {
+        return util::ErrStatus(
+            "thread_name metadata event without thread association");
+      }
+
+      auto it = event_.debug_annotations();
+      if (!it) {
+        return util::ErrStatus(
+            "thread_name metadata event without debug annotations");
+      }
+      protos::pbzero::DebugAnnotation::Decoder annotation(*it);
+      auto thread_name = annotation.string_value();
+      if (!thread_name.size)
+        return util::OkStatus();
+      auto thread_name_id = storage_->InternString(thread_name);
+      // Don't override system-provided names.
+      procs->SetThreadNameIfUnset(*utid_, thread_name_id);
+      return util::OkStatus();
+    }
+    if (strcmp(event_name.c_str(), "process_name") == 0) {
+      if (!upid_) {
+        return util::ErrStatus(
+            "process_name metadata event without process association");
+      }
+
+      auto it = event_.debug_annotations();
+      if (!it) {
+        return util::ErrStatus(
+            "process_name metadata event without debug annotations");
+      }
+      protos::pbzero::DebugAnnotation::Decoder annotation(*it);
+      auto process_name = annotation.string_value();
+      if (!process_name.size)
+        return util::OkStatus();
+      auto process_name_id = storage_->InternString(process_name);
+      // Don't override system-provided names.
+      procs->SetProcessNameIfUnset(*upid_, process_name_id);
+      return util::OkStatus();
+    }
+    // Other metadata events are proxied via the raw table for JSON export.
+    ParseLegacyEventAsRawEvent();
+    return util::OkStatus();
+  }
+
+  util::Status ParseLegacyEventAsRawEvent() {
+    if (!utid_)
+      return util::ErrStatus("raw legacy event without thread association");
+
+    RawId id = storage_->mutable_raw_table()
+                   ->Insert({ts_, parser_->raw_legacy_event_id_, 0, *utid_})
+                   .id;
+
+    ArgsTracker args(context_);
+    auto inserter = args.AddArgsTo(id);
+
+    inserter
+        .AddArg(parser_->legacy_event_category_key_id_,
+                Variadic::String(category_id_))
+        .AddArg(parser_->legacy_event_name_key_id_, Variadic::String(name_id_));
+
+    std::string phase_string(1, static_cast<char>(legacy_event_.phase()));
+    StringId phase_id = storage_->InternString(phase_string.c_str());
+    inserter.AddArg(parser_->legacy_event_phase_key_id_,
+                    Variadic::String(phase_id));
+
+    if (legacy_event_.has_duration_us()) {
+      inserter.AddArg(parser_->legacy_event_duration_ns_key_id_,
+                      Variadic::Integer(legacy_event_.duration_us() * 1000));
+    }
+
+    if (event_data_->thread_timestamp) {
+      inserter.AddArg(parser_->legacy_event_thread_timestamp_ns_key_id_,
+                      Variadic::Integer(event_data_->thread_timestamp));
+      if (legacy_event_.has_thread_duration_us()) {
+        inserter.AddArg(
+            parser_->legacy_event_thread_duration_ns_key_id_,
+            Variadic::Integer(legacy_event_.thread_duration_us() * 1000));
+      }
+    }
+
+    if (event_data_->thread_instruction_count) {
+      inserter.AddArg(parser_->legacy_event_thread_instruction_count_key_id_,
+                      Variadic::Integer(event_data_->thread_instruction_count));
+      if (legacy_event_.has_thread_instruction_delta()) {
+        inserter.AddArg(
+            parser_->legacy_event_thread_instruction_delta_key_id_,
+            Variadic::Integer(legacy_event_.thread_instruction_delta()));
+      }
+    }
+
+    if (legacy_event_.use_async_tts()) {
+      inserter.AddArg(parser_->legacy_event_use_async_tts_key_id_,
+                      Variadic::Boolean(true));
+    }
+
+    bool has_id = false;
+    if (legacy_event_.has_unscoped_id()) {
+      // Unscoped ids are either global or local depending on the phase. Pass
+      // them through as unscoped IDs to JSON export to preserve this behavior.
+      inserter.AddArg(parser_->legacy_event_unscoped_id_key_id_,
+                      Variadic::UnsignedInteger(legacy_event_.unscoped_id()));
+      has_id = true;
+    } else if (legacy_event_.has_global_id()) {
+      inserter.AddArg(parser_->legacy_event_global_id_key_id_,
+                      Variadic::UnsignedInteger(legacy_event_.global_id()));
+      has_id = true;
+    } else if (legacy_event_.has_local_id()) {
+      inserter.AddArg(parser_->legacy_event_local_id_key_id_,
+                      Variadic::UnsignedInteger(legacy_event_.local_id()));
+      has_id = true;
+    }
+
+    if (has_id && legacy_event_.has_id_scope() &&
+        legacy_event_.id_scope().size) {
+      inserter.AddArg(
+          parser_->legacy_event_id_scope_key_id_,
+          Variadic::String(storage_->InternString(legacy_event_.id_scope())));
+    }
+
+    // No need to parse legacy_event.instant_event_scope() because we import
+    // instant events into the slice table.
+
+    ParseTrackEventArgs(&inserter);
+    return util::OkStatus();
+  }
+
+  void ParseTrackEventArgs(BoundInserter* inserter) {
+    auto log_errors = [this](util::Status status) {
+      if (status.ok())
+        return;
+      // Log error but continue parsing the other args.
+      storage_->IncrementStats(stats::track_event_parser_errors);
+      PERFETTO_DLOG("%s", status.c_message());
+    };
+
+    for (auto it = event_.debug_annotations(); it; ++it) {
+      log_errors(ParseDebugAnnotationArgs(*it, inserter));
+    }
+
+    if (event_.has_task_execution()) {
+      log_errors(ParseTaskExecutionArgs(event_.task_execution(), inserter));
+    }
+    if (event_.has_log_message()) {
+      log_errors(ParseLogMessage(event_.log_message(), inserter));
+    }
+    if (event_.has_cc_scheduler_state()) {
+      ParseCcScheduler(event_.cc_scheduler_state(), inserter);
+    }
+    if (event_.has_chrome_user_event()) {
+      ParseChromeUserEvent(event_.chrome_user_event(), inserter);
+    }
+    if (event_.has_chrome_legacy_ipc()) {
+      ParseChromeLegacyIpc(event_.chrome_legacy_ipc(), inserter);
+    }
+    if (event_.has_chrome_keyed_service()) {
+      ParseChromeKeyedService(event_.chrome_keyed_service(), inserter);
+    }
+    if (event_.has_chrome_histogram_sample()) {
+      ParseChromeHistogramSample(event_.chrome_histogram_sample(), inserter);
+    }
+    if (event_.has_chrome_latency_info()) {
+      ParseChromeLatencyInfo(event_.chrome_latency_info(), inserter);
+    }
+
+    if (legacy_passthrough_utid_) {
+      inserter->AddArg(parser_->legacy_event_passthrough_utid_id_,
+                       Variadic::UnsignedInteger(*legacy_passthrough_utid_),
+                       ArgsTracker::UpdatePolicy::kSkipIfExists);
+    }
+
+    // TODO(eseckler): Parse legacy flow events into flow events table once we
+    // have a design for it.
+    if (legacy_event_.has_bind_id()) {
+      inserter->AddArg(parser_->legacy_event_bind_id_key_id_,
+                       Variadic::UnsignedInteger(legacy_event_.bind_id()));
+    }
+
+    if (legacy_event_.bind_to_enclosing()) {
+      inserter->AddArg(parser_->legacy_event_bind_to_enclosing_key_id_,
+                       Variadic::Boolean(true));
+    }
+
+    if (legacy_event_.flow_direction()) {
+      StringId value;
+      switch (legacy_event_.flow_direction()) {
+        case LegacyEvent::FLOW_IN:
+          value = parser_->flow_direction_value_in_id_;
+          break;
+        case LegacyEvent::FLOW_OUT:
+          value = parser_->flow_direction_value_out_id_;
+          break;
+        case LegacyEvent::FLOW_INOUT:
+          value = parser_->flow_direction_value_inout_id_;
+          break;
+        default:
+          PERFETTO_FATAL("Unknown flow direction: %d",
+                         legacy_event_.flow_direction());
+          break;
+      }
+      inserter->AddArg(parser_->legacy_event_flow_direction_key_id_,
+                       Variadic::String(value));
+    }
+  }
+
+  util::Status ParseDebugAnnotationArgs(ConstBytes debug_annotation,
+                                        BoundInserter* inserter) {
+    protos::pbzero::DebugAnnotation::Decoder annotation(debug_annotation);
+
+    StringId name_id = kNullStringId;
+
+    uint64_t name_iid = annotation.name_iid();
+    if (PERFETTO_LIKELY(name_iid)) {
+      auto* decoder = sequence_state_->LookupInternedMessage<
+          protos::pbzero::InternedData::kDebugAnnotationNamesFieldNumber,
+          protos::pbzero::DebugAnnotationName>(name_iid);
+      if (!decoder)
+        return util::ErrStatus("Debug annotation with invalid name_iid");
+
+      std::string name_prefixed =
+          SafeDebugAnnotationName(decoder->name().ToStdString());
+      name_id = storage_->InternString(base::StringView(name_prefixed));
+    } else if (annotation.has_name()) {
+      name_id = storage_->InternString(annotation.name());
+    } else {
+      return util::ErrStatus("Debug annotation without name");
+    }
+
+    if (annotation.has_bool_value()) {
+      inserter->AddArg(name_id, Variadic::Boolean(annotation.bool_value()));
+    } else if (annotation.has_uint_value()) {
+      inserter->AddArg(name_id,
+                       Variadic::UnsignedInteger(annotation.uint_value()));
+    } else if (annotation.has_int_value()) {
+      inserter->AddArg(name_id, Variadic::Integer(annotation.int_value()));
+    } else if (annotation.has_double_value()) {
+      inserter->AddArg(name_id, Variadic::Real(annotation.double_value()));
+    } else if (annotation.has_string_value()) {
+      inserter->AddArg(
+          name_id,
+          Variadic::String(storage_->InternString(annotation.string_value())));
+    } else if (annotation.has_pointer_value()) {
+      inserter->AddArg(name_id, Variadic::Pointer(annotation.pointer_value()));
+    } else if (annotation.has_legacy_json_value()) {
+      if (!json::IsJsonSupported())
+        return util::ErrStatus("Ignoring legacy_json_value (no json support)");
+
+      auto value = json::ParseJsonString(annotation.legacy_json_value());
+      auto name = storage_->GetString(name_id);
+      json::AddJsonValueToArgs(*value, name, name, storage_, inserter);
+    } else if (annotation.has_nested_value()) {
+      auto name = storage_->GetString(name_id);
+      ParseNestedValueArgs(annotation.nested_value(), name, name, inserter);
+    }
+
+    return util::OkStatus();
+  }
+
+  bool ParseNestedValueArgs(ConstBytes nested_value,
+                            base::StringView flat_key,
+                            base::StringView key,
+                            BoundInserter* inserter) {
+    protos::pbzero::DebugAnnotation::NestedValue::Decoder value(nested_value);
+    switch (value.nested_type()) {
+      case protos::pbzero::DebugAnnotation::NestedValue::UNSPECIFIED: {
+        auto flat_key_id = storage_->InternString(flat_key);
+        auto key_id = storage_->InternString(key);
+        // Leaf value.
+        if (value.has_bool_value()) {
+          inserter->AddArg(flat_key_id, key_id,
+                           Variadic::Boolean(value.bool_value()));
+          return true;
+        }
+        if (value.has_int_value()) {
+          inserter->AddArg(flat_key_id, key_id,
+                           Variadic::Integer(value.int_value()));
+          return true;
+        }
+        if (value.has_double_value()) {
+          inserter->AddArg(flat_key_id, key_id,
+                           Variadic::Real(value.double_value()));
+          return true;
+        }
+        if (value.has_string_value()) {
+          inserter->AddArg(
+              flat_key_id, key_id,
+              Variadic::String(storage_->InternString(value.string_value())));
+          return true;
+        }
+        return false;
+      }
+      case protos::pbzero::DebugAnnotation::NestedValue::DICT: {
+        auto key_it = value.dict_keys();
+        auto value_it = value.dict_values();
+        bool inserted = false;
+        for (; key_it && value_it; ++key_it, ++value_it) {
+          std::string child_name = (*key_it).ToStdString();
+          std::string child_flat_key =
+              flat_key.ToStdString() + "." + child_name;
+          std::string child_key = key.ToStdString() + "." + child_name;
+          inserted |=
+              ParseNestedValueArgs(*value_it, base::StringView(child_flat_key),
+                                   base::StringView(child_key), inserter);
+        }
+        return inserted;
+      }
+      case protos::pbzero::DebugAnnotation::NestedValue::ARRAY: {
+        bool inserted_any = false;
+        std::string array_key = key.ToStdString();
+        StringId array_key_id = storage_->InternString(key);
+        for (auto value_it = value.array_values(); value_it; ++value_it) {
+          size_t array_index = inserter->GetNextArrayEntryIndex(array_key_id);
+          std::string child_key =
+              array_key + "[" + std::to_string(array_index) + "]";
+          bool inserted = ParseNestedValueArgs(
+              *value_it, flat_key, base::StringView(child_key), inserter);
+          if (inserted)
+            inserter->IncrementArrayEntryIndex(array_key_id);
+          inserted_any |= inserted;
+        }
+        return inserted_any;
+      }
+    }
+    return false;
+  }
+
+  util::Status ParseTaskExecutionArgs(ConstBytes task_execution,
+                                      BoundInserter* inserter) {
+    protos::pbzero::TaskExecution::Decoder task(task_execution);
+    uint64_t iid = task.posted_from_iid();
+    if (!iid)
+      return util::ErrStatus("TaskExecution with invalid posted_from_iid");
+
+    auto* decoder = sequence_state_->LookupInternedMessage<
+        protos::pbzero::InternedData::kSourceLocationsFieldNumber,
+        protos::pbzero::SourceLocation>(iid);
+    if (!decoder)
+      return util::ErrStatus("TaskExecution with invalid posted_from_iid");
+
+    StringId file_name_id = kNullStringId;
+    StringId function_name_id = kNullStringId;
+    uint32_t line_number = 0;
+
+    file_name_id = storage_->InternString(decoder->file_name());
+    function_name_id = storage_->InternString(decoder->function_name());
+    line_number = decoder->line_number();
+
+    inserter->AddArg(parser_->task_file_name_args_key_id_,
+                     Variadic::String(file_name_id));
+    inserter->AddArg(parser_->task_function_name_args_key_id_,
+                     Variadic::String(function_name_id));
+    inserter->AddArg(parser_->task_line_number_args_key_id_,
+                     Variadic::UnsignedInteger(line_number));
+    return util::OkStatus();
+  }
+
+  util::Status ParseLogMessage(ConstBytes blob, BoundInserter* inserter) {
+    if (!utid_)
+      return util::ErrStatus("LogMessage without thread association");
+
+    protos::pbzero::LogMessage::Decoder message(blob);
+
+    StringId log_message_id = kNullStringId;
+
+    auto* decoder = sequence_state_->LookupInternedMessage<
+        protos::pbzero::InternedData::kLogMessageBodyFieldNumber,
+        protos::pbzero::LogMessageBody>(message.body_iid());
+    if (!decoder)
+      return util::ErrStatus("LogMessage with invalid body_iid");
+
+    log_message_id = storage_->InternString(decoder->body());
+
+    // TODO(nicomazz): LogMessage also contains the source of the message (file
+    // and line number). Android logs doesn't support this so far.
+    storage_->mutable_android_log_table()->Insert(
+        {ts_, *utid_,
+         /*priority*/ 0,
+         /*tag_id*/ kNullStringId,  // TODO(nicomazz): Abuse tag_id to display
+                                    // "file_name:line_number".
+         log_message_id});
+
+    inserter->AddArg(parser_->log_message_body_key_id_,
+                     Variadic::String(log_message_id));
+    // TODO(nicomazz): Add the source location as an argument.
+    return util::OkStatus();
+  }
+
+  void ParseCcScheduler(ConstBytes cc, BoundInserter* outer_inserter) {
+    parser_->proto_to_args_.InternProtoIntoArgsTable(
+        cc, ".perfetto.protos.ChromeCompositorSchedulerState", outer_inserter,
+        sequence_state_,
+        /* prefix= */ "cc_scheduler_state");
+  }
+
+  void ParseChromeUserEvent(protozero::ConstBytes chrome_user_event,
+                            BoundInserter* inserter) {
+    protos::pbzero::ChromeUserEvent::Decoder event(chrome_user_event);
+    if (event.has_action()) {
+      StringId action_id = storage_->InternString(event.action());
+      inserter->AddArg(parser_->chrome_user_event_action_args_key_id_,
+                       Variadic::String(action_id));
+    }
+    if (event.has_action_hash()) {
+      inserter->AddArg(parser_->chrome_user_event_action_hash_args_key_id_,
+                       Variadic::UnsignedInteger(event.action_hash()));
+    }
+  }
+
+  void ParseChromeLegacyIpc(protozero::ConstBytes chrome_legacy_ipc,
+                            BoundInserter* inserter) {
+    protos::pbzero::ChromeLegacyIpc::Decoder event(chrome_legacy_ipc);
+    if (event.has_message_class()) {
+      size_t message_class_index = static_cast<size_t>(event.message_class());
+      if (message_class_index >= parser_->chrome_legacy_ipc_class_ids_.size())
+        message_class_index = 0;
+      inserter->AddArg(
+          parser_->chrome_legacy_ipc_class_args_key_id_,
+          Variadic::String(
+              parser_->chrome_legacy_ipc_class_ids_[message_class_index]));
+    }
+    if (event.has_message_line()) {
+      inserter->AddArg(parser_->chrome_legacy_ipc_line_args_key_id_,
+                       Variadic::Integer(event.message_line()));
+    }
+  }
+
+  void ParseChromeKeyedService(protozero::ConstBytes chrome_keyed_service,
+                               BoundInserter* inserter) {
+    protos::pbzero::ChromeKeyedService::Decoder event(chrome_keyed_service);
+    if (event.has_name()) {
+      StringId action_id = storage_->InternString(event.name());
+      inserter->AddArg(parser_->chrome_keyed_service_name_args_key_id_,
+                       Variadic::String(action_id));
+    }
+  }
+
+  void ParseChromeLatencyInfo(protozero::ConstBytes chrome_latency_info,
+                              BoundInserter* inserter) {
+    parser_->proto_to_args_.InternProtoIntoArgsTable(
+        chrome_latency_info, ".perfetto.protos.ChromeLatencyInfo", inserter,
+        sequence_state_, "latency_info");
+  }
+
+  void ParseChromeHistogramSample(protozero::ConstBytes chrome_histogram_sample,
+                                  BoundInserter* inserter) {
+    protos::pbzero::ChromeHistogramSample::Decoder event(
+        chrome_histogram_sample);
+    if (event.has_name_hash()) {
+      uint64_t name_hash = static_cast<uint64_t>(event.name_hash());
+      inserter->AddArg(parser_->chrome_histogram_sample_name_hash_args_key_id_,
+                       Variadic::UnsignedInteger(name_hash));
+    }
+    if (event.has_name()) {
+      StringId name = storage_->InternString(event.name());
+      inserter->AddArg(parser_->chrome_keyed_service_name_args_key_id_,
+                       Variadic::String(name));
+    }
+    if (event.has_sample()) {
+      int64_t sample = static_cast<int64_t>(event.sample());
+      inserter->AddArg(parser_->chrome_histogram_sample_sample_args_key_id_,
+                       Variadic::Integer(sample));
+    }
+  }
+
+  TraceProcessorContext* context_;
+  TraceStorage* storage_;
+  TrackEventParser* parser_;
+  int64_t ts_;
+  TrackEventData* event_data_;
+  PacketSequenceStateGeneration* sequence_state_;
+  ConstBytes blob_;
+  TrackEvent::Decoder event_;
+  LegacyEvent::Decoder legacy_event_;
+  protos::pbzero::TrackEventDefaults::Decoder* defaults_;
+
+  // Importing state.
+  StringId category_id_;
+  StringId name_id_;
+  uint64_t track_uuid_;
+  TrackId track_id_;
+  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_;
+};
+
 TrackEventParser::TrackEventParser(TraceProcessorContext* context)
     : context_(context),
+      proto_to_args_(context_),
+      counter_name_thread_time_id_(
+          context->storage->InternString("thread_time")),
+      counter_name_thread_instruction_count_id_(
+          context->storage->InternString("thread_instruction_count")),
       task_file_name_args_key_id_(
           context->storage->InternString("task.posted_from.file_name")),
       task_function_name_args_key_id_(
@@ -155,6 +1244,8 @@
       flow_direction_value_inout_id_(context->storage->InternString("inout")),
       chrome_user_event_action_args_key_id_(
           context->storage->InternString("user_event.action")),
+      chrome_user_event_action_hash_args_key_id_(
+          context->storage->InternString("user_event.action_hash")),
       chrome_legacy_ipc_class_args_key_id_(
           context->storage->InternString("legacy_ipc.class")),
       chrome_legacy_ipc_line_args_key_id_(
@@ -167,6 +1258,22 @@
           context->storage->InternString("histogram_sample.name")),
       chrome_histogram_sample_sample_args_key_id_(
           context->storage->InternString("histogram_sample.sample")),
+      chrome_latency_info_trace_id_key_id_(
+          context->storage->InternString("latency_info.trace_id")),
+      chrome_latency_info_step_key_id_(
+          context->storage->InternString("latency_info.step")),
+      chrome_latency_info_frame_tree_node_id_key_id_(
+          context->storage->InternString("latency_info.frame_tree_node_id")),
+      chrome_latency_info_step_ids_{
+          {context->storage->InternString("STEP_UNSPECIFIED"),
+           context->storage->InternString(
+               "STEP_HANDLE_INPUT_EVENT_MAIN_COMMIT"),
+           context->storage->InternString("STEP_MAIN_THREAD_SCROLL_UPDATE"),
+           context->storage->InternString("STEP_SEND_INPUT_EVENT_UI"),
+           context->storage->InternString("STEP_HANDLE_INPUT_EVENT_MAIN"),
+           context->storage->InternString("STEP_HANDLE_INPUT_EVENT_IMPL"),
+           context->storage->InternString("STEP_SWAP_BUFFERS"),
+           context->storage->InternString("STEP_DRAW_AND_SWAP")}},
       chrome_legacy_ipc_class_ids_{
           {context->storage->InternString("UNSPECIFIED"),
            context->storage->InternString("AUTOMATION"),
@@ -230,7 +1337,38 @@
            context_->storage->InternString("CompositorTileWorker&"),
            context_->storage->InternString("ServiceWorkerThread&"),
            context_->storage->InternString("MemoryInfra"),
-           context_->storage->InternString("StackSamplingProfiler")}} {}
+           context_->storage->InternString("StackSamplingProfiler")}},
+      counter_unit_ids_{{kNullStringId, context_->storage->InternString("ns"),
+                         context_->storage->InternString("count"),
+                         context_->storage->InternString("bytes")}} {
+  auto status = proto_to_args_.AddProtoFileDescriptor(
+      kTrackEventDescriptor.data(), kTrackEventDescriptor.size());
+  PERFETTO_DCHECK(status.ok());
+
+  // Switch |source_location_iid| into its interned data variant.
+  proto_to_args_.AddParsingOverride(
+      "begin_impl_frame_args.current_args.source_location_iid",
+      [](const ProtoToArgsTable::ParsingOverrideState& state,
+         const protozero::Field& field, BoundInserter* inserter) {
+        return MaybeParseSourceLocation("begin_impl_frame_args.current_args",
+                                        state, field, inserter);
+      });
+  proto_to_args_.AddParsingOverride(
+      "begin_impl_frame_args.last_args.source_location_iid",
+      [](const ProtoToArgsTable::ParsingOverrideState& state,
+         const protozero::Field& field, BoundInserter* inserter) {
+        return MaybeParseSourceLocation("begin_impl_frame_args.last_args",
+                                        state, field, inserter);
+      });
+  proto_to_args_.AddParsingOverride(
+      "begin_frame_observer_state.last_begin_frame_args.source_location_iid",
+      [](const ProtoToArgsTable::ParsingOverrideState& state,
+         const protozero::Field& field, BoundInserter* inserter) {
+        return MaybeParseSourceLocation(
+            "begin_frame_observer_state.last_begin_frame_args", state, field,
+            inserter);
+      });
+}
 
 void TrackEventParser::ParseTrackDescriptor(
     protozero::ConstBytes track_descriptor) {
@@ -241,16 +1379,16 @@
   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());
+  } else if (decoder.has_process()) {
+    UniquePid upid = ParseProcessDescriptor(decoder.process());
+    if (decoder.has_chrome_process())
+      ParseChromeProcessDescriptor(upid, decoder.chrome_process());
+  } else if (decoder.has_counter()) {
+    ParseCounterDescriptor(track_id, decoder.counter());
   }
 
   if (decoder.has_name()) {
@@ -265,7 +1403,7 @@
   protos::pbzero::ProcessDescriptor::Decoder decoder(process_descriptor);
   UniquePid upid = context_->process_tracker->GetOrCreateProcess(
       static_cast<uint32_t>(decoder.pid()));
-  if (decoder.has_process_name()) {
+  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()));
@@ -306,7 +1444,7 @@
       static_cast<uint32_t>(decoder.tid()),
       static_cast<uint32_t>(decoder.pid()));
   StringId name_id = kNullStringId;
-  if (decoder.has_thread_name()) {
+  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.
@@ -341,923 +1479,47 @@
   context_->process_tracker->SetThreadNameIfUnset(utid, name_id);
 }
 
-void TrackEventParser::ParseTrackEvent(
-    int64_t ts,
-    int64_t tts,
-    int64_t ticount,
-    PacketSequenceStateGeneration* sequence_state,
-    ConstBytes blob) {
-  using LegacyEvent = protos::pbzero::TrackEvent::LegacyEvent;
+void TrackEventParser::ParseCounterDescriptor(
+    TrackId track_id,
+    protozero::ConstBytes counter_descriptor) {
+  using protos::pbzero::CounterDescriptor;
 
-  protos::pbzero::TrackEventDefaults::Decoder* defaults = nullptr;
-  auto* packet_defaults_view = sequence_state->GetTracePacketDefaultsView();
-  if (packet_defaults_view) {
-    auto* track_event_defaults_view =
-        packet_defaults_view
-            ->GetOrCreateSubmessageView<protos::pbzero::TracePacketDefaults,
-                                        protos::pbzero::TracePacketDefaults::
-                                            kTrackEventDefaultsFieldNumber>();
-    if (track_event_defaults_view) {
-      defaults = track_event_defaults_view
-                     ->GetOrCreateDecoder<protos::pbzero::TrackEventDefaults>();
-    }
+  CounterDescriptor::Decoder decoder(counter_descriptor);
+  auto* counter_tracks = context_->storage->mutable_counter_track_table();
+
+  size_t unit_index = static_cast<size_t>(decoder.unit());
+  if (unit_index >= counter_unit_ids_.size())
+    unit_index = CounterDescriptor::UNIT_UNSPECIFIED;
+
+  switch (decoder.type()) {
+    case CounterDescriptor::COUNTER_UNSPECIFIED:
+      break;
+    case CounterDescriptor::COUNTER_THREAD_TIME_NS:
+      unit_index = CounterDescriptor::UNIT_TIME_NS;
+      counter_tracks->mutable_name()->Set(
+          *counter_tracks->id().IndexOf(track_id),
+          counter_name_thread_time_id_);
+      break;
+    case CounterDescriptor::COUNTER_THREAD_INSTRUCTION_COUNT:
+      unit_index = CounterDescriptor::UNIT_COUNT;
+      counter_tracks->mutable_name()->Set(
+          *counter_tracks->id().IndexOf(track_id),
+          counter_name_thread_instruction_count_id_);
+      break;
   }
 
-  protos::pbzero::TrackEvent::Decoder event(blob.data, blob.size);
+  counter_tracks->mutable_unit()->Set(*counter_tracks->id().IndexOf(track_id),
+                                      counter_unit_ids_[unit_index]);
+}
 
-  const auto legacy_event_blob = event.legacy_event();
-  LegacyEvent::Decoder legacy_event(legacy_event_blob.data,
-                                    legacy_event_blob.size);
-
-  // TODO(eseckler): This legacy event field will eventually be replaced by
-  // fields in TrackEvent itself.
-  if (PERFETTO_UNLIKELY(!event.type() && !legacy_event.has_phase())) {
+void TrackEventParser::ParseTrackEvent(int64_t ts,
+                                       TrackEventData* event_data,
+                                       ConstBytes blob) {
+  util::Status status =
+      EventImporter(this, ts, event_data, std::move(blob)).Import();
+  if (!status.ok()) {
     context_->storage->IncrementStats(stats::track_event_parser_errors);
-    PERFETTO_DLOG("TrackEvent without type or phase");
-    return;
-  }
-
-  ProcessTracker* procs = context_->process_tracker.get();
-  TraceStorage* storage = context_->storage.get();
-  TrackTracker* track_tracker = context_->track_tracker.get();
-  SliceTracker* slice_tracker = context_->slice_tracker.get();
-
-  std::vector<uint64_t> category_iids;
-  for (auto it = event.category_iids(); it; ++it) {
-    category_iids.push_back(*it);
-  }
-  std::vector<protozero::ConstChars> category_strings;
-  for (auto it = event.categories(); it; ++it) {
-    category_strings.push_back(*it);
-  }
-
-  StringId category_id = kNullStringId;
-
-  // If there's a single category, we can avoid building a concatenated
-  // string.
-  if (PERFETTO_LIKELY(category_iids.size() == 1 && category_strings.empty())) {
-    auto* decoder = sequence_state->LookupInternedMessage<
-        protos::pbzero::InternedData::kEventCategoriesFieldNumber,
-        protos::pbzero::EventCategory>(category_iids[0]);
-    if (decoder)
-      category_id = storage->InternString(decoder->name());
-  } else if (category_iids.empty() && category_strings.size() == 1) {
-    category_id = storage->InternString(category_strings[0]);
-  } else if (category_iids.size() + category_strings.size() > 1) {
-    // We concatenate the category strings together since we currently only
-    // support a single "cat" column.
-    // TODO(eseckler): Support multi-category events in the table schema.
-    std::string categories;
-    for (uint64_t iid : category_iids) {
-      auto* decoder = sequence_state->LookupInternedMessage<
-          protos::pbzero::InternedData::kEventCategoriesFieldNumber,
-          protos::pbzero::EventCategory>(iid);
-      if (!decoder)
-        continue;
-      base::StringView name = decoder->name();
-      if (!categories.empty())
-        categories.append(",");
-      categories.append(name.data(), name.size());
-    }
-    for (const protozero::ConstChars& cat : category_strings) {
-      if (!categories.empty())
-        categories.append(",");
-      categories.append(cat.data, cat.size);
-    }
-    if (!categories.empty())
-      category_id = storage->InternString(base::StringView(categories));
-  }
-
-  StringId name_id = kNullStringId;
-
-  uint64_t name_iid = event.name_iid();
-  if (!name_iid)
-    name_iid = legacy_event.name_iid();
-
-  if (PERFETTO_LIKELY(name_iid)) {
-    auto* decoder = sequence_state->LookupInternedMessage<
-        protos::pbzero::InternedData::kEventNamesFieldNumber,
-        protos::pbzero::EventName>(name_iid);
-    if (decoder)
-      name_id = storage->InternString(decoder->name());
-  } else if (event.has_name()) {
-    name_id = storage->InternString(event.name());
-  }
-
-  // Consider track_uuid from the packet and TrackEventDefaults, fall back to
-  // the default descriptor track (uuid 0).
-  uint64_t track_uuid =
-      event.has_track_uuid()
-          ? event.track_uuid()
-          : (defaults && defaults->has_track_uuid() ? defaults->track_uuid()
-                                                    : 0u);
-
-  TrackId track_id;
-  base::Optional<UniqueTid> utid;
-  base::Optional<UniqueTid> upid;
-
-  // Determine track from track_uuid specified in either TrackEvent or
-  // 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);
-    if (!opt_track_id) {
-      storage->IncrementStats(stats::track_event_parser_errors);
-      PERFETTO_DLOG("TrackEvent with unknown track_uuid %" PRIu64, track_uuid);
-      return;
-    }
-    track_id = *opt_track_id;
-
-    auto thread_track_row =
-        context_->storage->thread_track_table().id().IndexOf(track_id);
-    if (thread_track_row) {
-      utid = storage->thread_track_table().utid()[*thread_track_row];
-      upid = storage->thread_table().upid()[*utid];
-    } else {
-      auto process_track_row =
-          context_->storage->process_track_table().id().IndexOf(track_id);
-      if (process_track_row)
-        upid = storage->process_track_table().upid()[*process_track_row];
-    }
-  } 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())
-      pid = static_cast<uint32_t>(legacy_event.pid_override());
-    if (legacy_event.has_tid_override())
-      tid = static_cast<uint32_t>(legacy_event.tid_override());
-
-    utid = procs->UpdateThread(tid, pid);
-    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 utid through to the json export, we
-  // store it in an arg.
-  base::Optional<UniqueTid> legacy_passthrough_utid;
-
-  // 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;
-  if (legacy_event.has_phase()) {
-    phase = legacy_event.phase();
-
-    switch (phase) {
-      case 'b':
-      case 'e':
-      case 'n': {
-        // Intern tracks for legacy async events based on legacy event ids.
-        int64_t source_id = 0;
-        bool source_id_is_process_scoped = false;
-        if (legacy_event.has_unscoped_id()) {
-          source_id = static_cast<int64_t>(legacy_event.unscoped_id());
-        } else if (legacy_event.has_global_id()) {
-          source_id = static_cast<int64_t>(legacy_event.global_id());
-        } else if (legacy_event.has_local_id()) {
-          if (!upid) {
-            storage->IncrementStats(stats::track_event_parser_errors);
-            PERFETTO_DLOG(
-                "TrackEvent with local_id without process association");
-            return;
-          }
-
-          source_id = static_cast<int64_t>(legacy_event.local_id());
-          source_id_is_process_scoped = true;
-        } else {
-          storage->IncrementStats(stats::track_event_parser_errors);
-          PERFETTO_DLOG("Async LegacyEvent without ID");
-          return;
-        }
-
-        // Catapult treats nestable async events of different categories with
-        // the same ID as separate tracks. We replicate the same behavior here.
-        StringId id_scope = category_id;
-        if (legacy_event.has_id_scope()) {
-          std::string concat = storage->GetString(category_id).ToStdString() +
-                               ":" + legacy_event.id_scope().ToStdString();
-          id_scope = storage->InternString(base::StringView(concat));
-        }
-
-        track_id = context_->track_tracker->InternLegacyChromeAsyncTrack(
-            name_id, upid ? *upid : 0, source_id, source_id_is_process_scoped,
-            id_scope);
-        legacy_passthrough_utid = utid;
-        break;
-      }
-      case 'i':
-      case 'I': {
-        // Intern tracks for global or process-scoped legacy instant events.
-        switch (legacy_event.instant_event_scope()) {
-          case LegacyEvent::SCOPE_UNSPECIFIED:
-          case LegacyEvent::SCOPE_THREAD:
-            // Thread-scoped legacy instant events already have the right track
-            // based on the tid/pid of the sequence.
-            if (!utid) {
-              storage->IncrementStats(stats::track_event_parser_errors);
-              PERFETTO_DLOG(
-                  "Thread-scoped instant event without thread association");
-              return;
-            }
-            break;
-          case LegacyEvent::SCOPE_GLOBAL:
-            track_id = context_->track_tracker
-                           ->GetOrCreateLegacyChromeGlobalInstantTrack();
-            legacy_passthrough_utid = utid;
-            break;
-          case LegacyEvent::SCOPE_PROCESS:
-            if (!upid) {
-              storage->IncrementStats(stats::track_event_parser_errors);
-              PERFETTO_DLOG(
-                  "Process-scoped instant event without process association");
-              return;
-            }
-
-            track_id =
-                context_->track_tracker->InternLegacyChromeProcessInstantTrack(
-                    *upid);
-            legacy_passthrough_utid = utid;
-            break;
-        }
-        break;
-      }
-      default:
-        break;
-    }
-  } else {
-    switch (event.type()) {
-      case protos::pbzero::TrackEvent::TYPE_SLICE_BEGIN:
-        phase = utid ? 'B' : 'b';
-        break;
-      case protos::pbzero::TrackEvent::TYPE_SLICE_END:
-        phase = utid ? 'E' : 'e';
-        break;
-      case protos::pbzero::TrackEvent::TYPE_INSTANT:
-        phase = utid ? 'i' : 'n';
-        break;
-      default:
-        PERFETTO_FATAL("unexpected event type %d", event.type());
-        return;
-    }
-  }
-
-  auto args_callback = [this, &event, &legacy_event, sequence_state, ts, utid,
-                        legacy_passthrough_utid](
-                           ArgsTracker::BoundInserter* inserter) {
-    for (auto it = event.debug_annotations(); it; ++it) {
-      ParseDebugAnnotationArgs(*it, sequence_state, inserter);
-    }
-
-    if (event.has_task_execution()) {
-      ParseTaskExecutionArgs(event.task_execution(), sequence_state, inserter);
-    }
-
-    if (event.has_log_message()) {
-      ParseLogMessage(event.log_message(), sequence_state, ts, utid, inserter);
-    }
-    if (event.has_cc_scheduler_state()) {
-      ParseCcScheduler(event.cc_scheduler_state(), sequence_state, inserter);
-    }
-    if (event.has_chrome_user_event()) {
-      ParseChromeUserEvent(event.chrome_user_event(), inserter);
-    }
-    if (event.has_chrome_legacy_ipc()) {
-      ParseChromeLegacyIpc(event.chrome_legacy_ipc(), inserter);
-    }
-    if (event.has_chrome_keyed_service()) {
-      ParseChromeKeyedService(event.chrome_keyed_service(), inserter);
-    }
-    if (event.has_chrome_histogram_sample()) {
-      ParseChromeHistogramSample(event.chrome_histogram_sample(), inserter);
-    }
-
-    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
-    // have a design for it.
-    if (legacy_event.has_bind_id()) {
-      inserter->AddArg(legacy_event_bind_id_key_id_,
-                       Variadic::UnsignedInteger(legacy_event.bind_id()));
-    }
-
-    if (legacy_event.bind_to_enclosing()) {
-      inserter->AddArg(legacy_event_bind_to_enclosing_key_id_,
-                       Variadic::Boolean(true));
-    }
-
-    if (legacy_event.flow_direction()) {
-      StringId value;
-      switch (legacy_event.flow_direction()) {
-        case protos::pbzero::TrackEvent::LegacyEvent::FLOW_IN:
-          value = flow_direction_value_in_id_;
-          break;
-        case protos::pbzero::TrackEvent::LegacyEvent::FLOW_OUT:
-          value = flow_direction_value_out_id_;
-          break;
-        case protos::pbzero::TrackEvent::LegacyEvent::FLOW_INOUT:
-          value = flow_direction_value_inout_id_;
-          break;
-        default:
-          PERFETTO_FATAL("Unknown flow direction: %d",
-                         legacy_event.flow_direction());
-          break;
-      }
-      inserter->AddArg(legacy_event_flow_direction_key_id_,
-                       Variadic::String(value));
-    }
-  };
-
-  switch (static_cast<char>(phase)) {
-    case 'B': {  // TRACE_EVENT_PHASE_BEGIN.
-      if (!utid) {
-        storage->IncrementStats(stats::track_event_parser_errors);
-        PERFETTO_DLOG("TrackEvent with phase B without thread association");
-        return;
-      }
-
-      auto opt_slice_id = slice_tracker->Begin(ts, track_id, category_id,
-                                               name_id, args_callback);
-      if (opt_slice_id.has_value()) {
-        auto* thread_slices = storage->mutable_thread_slices();
-        PERFETTO_DCHECK(!thread_slices->slice_count() ||
-                        thread_slices->slice_ids().back() <
-                            opt_slice_id.value());
-        thread_slices->AddThreadSlice(opt_slice_id.value(), tts,
-                                      kPendingThreadDuration, ticount,
-                                      kPendingThreadInstructionDelta);
-      }
-      break;
-    }
-    case 'E': {  // TRACE_EVENT_PHASE_END.
-      if (!utid) {
-        storage->IncrementStats(stats::track_event_parser_errors);
-        PERFETTO_DLOG("TrackEvent with phase E without thread association");
-        return;
-      }
-
-      auto opt_slice_id =
-          slice_tracker->End(ts, track_id, category_id, name_id, args_callback);
-      if (opt_slice_id.has_value()) {
-        auto* thread_slices = storage->mutable_thread_slices();
-        thread_slices->UpdateThreadDeltasForSliceId(opt_slice_id.value(), tts,
-                                                    ticount);
-      }
-      break;
-    }
-    case 'X': {  // TRACE_EVENT_PHASE_COMPLETE.
-      if (!utid) {
-        storage->IncrementStats(stats::track_event_parser_errors);
-        PERFETTO_DLOG("TrackEvent with phase X without thread association");
-        return;
-      }
-
-      auto duration_ns = legacy_event.duration_us() * 1000;
-      if (duration_ns < 0)
-        return;
-      auto opt_slice_id = slice_tracker->Scoped(
-          ts, track_id, category_id, name_id, duration_ns, args_callback);
-      if (opt_slice_id.has_value()) {
-        auto* thread_slices = storage->mutable_thread_slices();
-        PERFETTO_DCHECK(!thread_slices->slice_count() ||
-                        thread_slices->slice_ids().back() <
-                            opt_slice_id.value());
-        auto thread_duration_ns = legacy_event.thread_duration_us() * 1000;
-        thread_slices->AddThreadSlice(opt_slice_id.value(), tts,
-                                      thread_duration_ns, ticount,
-                                      legacy_event.thread_instruction_delta());
-      }
-      break;
-    }
-    case 'i':
-    case 'I': {  // TRACE_EVENT_PHASE_INSTANT.
-      // Handle instant events as slices with zero duration, so that they end
-      // up nested underneath their parent slices.
-      int64_t duration_ns = 0;
-      int64_t tidelta = 0;
-
-      switch (legacy_event.instant_event_scope()) {
-        case LegacyEvent::SCOPE_UNSPECIFIED:
-        case LegacyEvent::SCOPE_THREAD: {
-          auto opt_slice_id = slice_tracker->Scoped(
-              ts, track_id, category_id, name_id, duration_ns, args_callback);
-          if (opt_slice_id.has_value()) {
-            auto* thread_slices = storage->mutable_thread_slices();
-            PERFETTO_DCHECK(!thread_slices->slice_count() ||
-                            thread_slices->slice_ids().back() <
-                                opt_slice_id.value());
-            thread_slices->AddThreadSlice(opt_slice_id.value(), tts,
-                                          duration_ns, ticount, tidelta);
-          }
-          break;
-        }
-        case LegacyEvent::SCOPE_GLOBAL: {
-          slice_tracker->Scoped(ts, track_id, category_id, name_id, duration_ns,
-                                args_callback);
-          break;
-        }
-        case LegacyEvent::SCOPE_PROCESS: {
-          slice_tracker->Scoped(ts, track_id, category_id, name_id, duration_ns,
-                                args_callback);
-          break;
-        }
-        default: {
-          PERFETTO_FATAL("Unknown instant event scope: %u",
-                         legacy_event.instant_event_scope());
-          break;
-        }
-      }
-      break;
-    }
-    case 'b': {  // TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN
-      auto opt_slice_id = slice_tracker->Begin(ts, track_id, category_id,
-                                               name_id, args_callback);
-      // For the time beeing, we only create vtrack slice rows if we need to
-      // store thread timestamps/counters.
-      if (legacy_event.use_async_tts() && opt_slice_id.has_value()) {
-        auto* vtrack_slices = storage->mutable_virtual_track_slices();
-        PERFETTO_DCHECK(!vtrack_slices->slice_count() ||
-                        vtrack_slices->slice_ids().back() <
-                            opt_slice_id.value());
-        vtrack_slices->AddVirtualTrackSlice(opt_slice_id.value(), tts,
-                                            kPendingThreadDuration, ticount,
-                                            kPendingThreadInstructionDelta);
-      }
-      break;
-    }
-    case 'e': {  // TRACE_EVENT_PHASE_NESTABLE_ASYNC_END
-      auto opt_slice_id =
-          slice_tracker->End(ts, track_id, category_id, name_id, args_callback);
-      if (legacy_event.use_async_tts() && opt_slice_id.has_value()) {
-        auto* vtrack_slices = storage->mutable_virtual_track_slices();
-        vtrack_slices->UpdateThreadDeltasForSliceId(opt_slice_id.value(), tts,
-                                                    ticount);
-      }
-      break;
-    }
-    case 'n': {  // TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT
-      // Handle instant events as slices with zero duration, so that they end up
-      // nested underneath their parent slices.
-      int64_t duration_ns = 0;
-      int64_t tidelta = 0;
-      auto opt_slice_id = slice_tracker->Scoped(
-          ts, track_id, category_id, name_id, duration_ns, args_callback);
-      if (legacy_event.use_async_tts() && opt_slice_id.has_value()) {
-        auto* vtrack_slices = storage->mutable_virtual_track_slices();
-        PERFETTO_DCHECK(!vtrack_slices->slice_count() ||
-                        vtrack_slices->slice_ids().back() <
-                            opt_slice_id.value());
-        vtrack_slices->AddVirtualTrackSlice(opt_slice_id.value(), tts,
-                                            duration_ns, ticount, tidelta);
-      }
-      break;
-    }
-    case 'M': {  // TRACE_EVENT_PHASE_METADATA (process and thread names).
-      // Parse process and thread names from correspondingly named events.
-      // TODO(eseckler): Also consider names from process/thread descriptors.
-      NullTermStringView event_name = storage->GetString(name_id);
-      PERFETTO_DCHECK(event_name.data());
-      if (strcmp(event_name.c_str(), "thread_name") == 0) {
-        if (!utid) {
-          storage->IncrementStats(stats::track_event_parser_errors);
-          PERFETTO_DLOG(
-              "thread_name metadata event without thread association");
-          return;
-        }
-
-        auto it = event.debug_annotations();
-        if (!it)
-          break;
-        protos::pbzero::DebugAnnotation::Decoder annotation(*it);
-        auto thread_name = annotation.string_value();
-        if (!thread_name.size)
-          break;
-        auto thread_name_id = storage->InternString(thread_name);
-        // Don't override system-provided names.
-        procs->SetThreadNameIfUnset(*utid, thread_name_id);
-        break;
-      }
-      if (strcmp(event_name.c_str(), "process_name") == 0) {
-        if (!upid) {
-          storage->IncrementStats(stats::track_event_parser_errors);
-          PERFETTO_DLOG(
-              "process_name metadata event without process association");
-          return;
-        }
-
-        auto it = event.debug_annotations();
-        if (!it)
-          break;
-        protos::pbzero::DebugAnnotation::Decoder annotation(*it);
-        auto process_name = annotation.string_value();
-        if (!process_name.size)
-          break;
-        auto process_name_id = storage->InternString(process_name);
-        // Don't override system-provided names.
-        procs->SetProcessNameIfUnset(*upid, process_name_id);
-        break;
-      }
-      // Other metadata events are proxied via the raw table for JSON export.
-      ParseLegacyEventAsRawEvent(ts, tts, ticount, utid, category_id, name_id,
-                                 legacy_event, args_callback);
-      break;
-    }
-    default: {
-      // Other events are proxied via the raw table for JSON export.
-      ParseLegacyEventAsRawEvent(ts, tts, ticount, utid, category_id, name_id,
-                                 legacy_event, args_callback);
-    }
-  }
-}
-
-void TrackEventParser::ParseLegacyEventAsRawEvent(
-    int64_t ts,
-    int64_t tts,
-    int64_t ticount,
-    base::Optional<UniqueTid> utid,
-    StringId category_id,
-    StringId name_id,
-    const protos::pbzero::TrackEvent::LegacyEvent::Decoder& legacy_event,
-    SliceTracker::SetArgsCallback slice_args_callback) {
-  if (!utid) {
-    context_->storage->IncrementStats(stats::track_event_parser_errors);
-    PERFETTO_DLOG("raw legacy event without thread association");
-    return;
-  }
-
-  RawId id = context_->storage->mutable_raw_table()->Insert(
-      {ts, raw_legacy_event_id_, 0, *utid});
-
-  ArgsTracker args(context_);
-  auto inserter = args.AddArgsTo(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());
-  inserter.AddArg(legacy_event_phase_key_id_, Variadic::String(phase_id));
-
-  if (legacy_event.has_duration_us()) {
-    inserter.AddArg(legacy_event_duration_ns_key_id_,
-                    Variadic::Integer(legacy_event.duration_us() * 1000));
-  }
-
-  if (tts) {
-    inserter.AddArg(legacy_event_thread_timestamp_ns_key_id_,
-                    Variadic::Integer(tts));
-    if (legacy_event.has_thread_duration_us()) {
-      inserter.AddArg(
-          legacy_event_thread_duration_ns_key_id_,
-          Variadic::Integer(legacy_event.thread_duration_us() * 1000));
-    }
-  }
-
-  if (ticount) {
-    inserter.AddArg(legacy_event_thread_instruction_count_key_id_,
-                    Variadic::Integer(tts));
-    if (legacy_event.has_thread_instruction_delta()) {
-      inserter.AddArg(
-          legacy_event_thread_instruction_delta_key_id_,
-          Variadic::Integer(legacy_event.thread_instruction_delta()));
-    }
-  }
-
-  if (legacy_event.use_async_tts()) {
-    inserter.AddArg(legacy_event_use_async_tts_key_id_,
-                    Variadic::Boolean(true));
-  }
-
-  bool has_id = false;
-  if (legacy_event.has_unscoped_id()) {
-    // Unscoped ids are either global or local depending on the phase. Pass them
-    // through as unscoped IDs to JSON export to preserve this behavior.
-    inserter.AddArg(legacy_event_unscoped_id_key_id_,
-                    Variadic::UnsignedInteger(legacy_event.unscoped_id()));
-    has_id = true;
-  } else if (legacy_event.has_global_id()) {
-    inserter.AddArg(legacy_event_global_id_key_id_,
-                    Variadic::UnsignedInteger(legacy_event.global_id()));
-    has_id = true;
-  } else if (legacy_event.has_local_id()) {
-    inserter.AddArg(legacy_event_local_id_key_id_,
-                    Variadic::UnsignedInteger(legacy_event.local_id()));
-    has_id = true;
-  }
-
-  if (has_id && legacy_event.has_id_scope() && legacy_event.id_scope().size) {
-    inserter.AddArg(legacy_event_id_scope_key_id_,
-                    Variadic::String(context_->storage->InternString(
-                        legacy_event.id_scope())));
-  }
-
-  // No need to parse legacy_event.instant_event_scope() because we import
-  // instant events into the slice table.
-
-  slice_args_callback(&inserter);
-}
-
-void TrackEventParser::ParseDebugAnnotationArgs(
-    ConstBytes debug_annotation,
-    PacketSequenceStateGeneration* sequence_state,
-    ArgsTracker::BoundInserter* inserter) {
-  TraceStorage* storage = context_->storage.get();
-
-  protos::pbzero::DebugAnnotation::Decoder annotation(debug_annotation.data,
-                                                      debug_annotation.size);
-
-  StringId name_id = kNullStringId;
-
-  uint64_t name_iid = annotation.name_iid();
-  if (PERFETTO_LIKELY(name_iid)) {
-    auto* decoder = sequence_state->LookupInternedMessage<
-        protos::pbzero::InternedData::kDebugAnnotationNamesFieldNumber,
-        protos::pbzero::DebugAnnotationName>(name_iid);
-    if (!decoder)
-      return;
-
-    std::string name_prefixed =
-        SafeDebugAnnotationName(decoder->name().ToStdString());
-    name_id = storage->InternString(base::StringView(name_prefixed));
-  } else if (annotation.has_name()) {
-    name_id = storage->InternString(annotation.name());
-  } else {
-    context_->storage->IncrementStats(stats::track_event_parser_errors);
-    PERFETTO_DLOG("Debug annotation without name");
-    return;
-  }
-
-  if (annotation.has_bool_value()) {
-    inserter->AddArg(name_id, Variadic::Boolean(annotation.bool_value()));
-  } else if (annotation.has_uint_value()) {
-    inserter->AddArg(name_id,
-                     Variadic::UnsignedInteger(annotation.uint_value()));
-  } else if (annotation.has_int_value()) {
-    inserter->AddArg(name_id, Variadic::Integer(annotation.int_value()));
-  } else if (annotation.has_double_value()) {
-    inserter->AddArg(name_id, Variadic::Real(annotation.double_value()));
-  } else if (annotation.has_string_value()) {
-    inserter->AddArg(
-        name_id,
-        Variadic::String(storage->InternString(annotation.string_value())));
-  } else if (annotation.has_pointer_value()) {
-    inserter->AddArg(name_id, Variadic::Pointer(annotation.pointer_value()));
-  } else if (annotation.has_legacy_json_value()) {
-    inserter->AddArg(
-        name_id,
-        Variadic::Json(storage->InternString(annotation.legacy_json_value())));
-  } else if (annotation.has_nested_value()) {
-    auto name = storage->GetString(name_id);
-    ParseNestedValueArgs(annotation.nested_value(), name, name, inserter);
-  }
-}
-
-void TrackEventParser::ParseNestedValueArgs(
-    ConstBytes nested_value,
-    base::StringView flat_key,
-    base::StringView key,
-    ArgsTracker::BoundInserter* inserter) {
-  protos::pbzero::DebugAnnotation::NestedValue::Decoder value(
-      nested_value.data, nested_value.size);
-  switch (value.nested_type()) {
-    case protos::pbzero::DebugAnnotation::NestedValue::UNSPECIFIED: {
-      auto flat_key_id = context_->storage->InternString(flat_key);
-      auto key_id = context_->storage->InternString(key);
-      // Leaf value.
-      if (value.has_bool_value()) {
-        inserter->AddArg(flat_key_id, key_id,
-                         Variadic::Boolean(value.bool_value()));
-      } else if (value.has_int_value()) {
-        inserter->AddArg(flat_key_id, key_id,
-                         Variadic::Integer(value.int_value()));
-      } else if (value.has_double_value()) {
-        inserter->AddArg(flat_key_id, key_id,
-                         Variadic::Real(value.double_value()));
-      } else if (value.has_string_value()) {
-        inserter->AddArg(flat_key_id, key_id,
-                         Variadic::String(context_->storage->InternString(
-                             value.string_value())));
-      }
-      break;
-    }
-    case protos::pbzero::DebugAnnotation::NestedValue::DICT: {
-      auto key_it = value.dict_keys();
-      auto value_it = value.dict_values();
-      for (; key_it && value_it; ++key_it, ++value_it) {
-        std::string child_name = (*key_it).ToStdString();
-        std::string child_flat_key = flat_key.ToStdString() + "." + child_name;
-        std::string child_key = key.ToStdString() + "." + child_name;
-        ParseNestedValueArgs(*value_it, base::StringView(child_flat_key),
-                             base::StringView(child_key), inserter);
-      }
-      break;
-    }
-    case protos::pbzero::DebugAnnotation::NestedValue::ARRAY: {
-      int child_index = 0;
-      std::string child_flat_key = flat_key.ToStdString();
-      for (auto value_it = value.array_values(); value_it;
-           ++value_it, ++child_index) {
-        std::string child_key =
-            key.ToStdString() + "[" + std::to_string(child_index) + "]";
-        ParseNestedValueArgs(*value_it, base::StringView(child_flat_key),
-                             base::StringView(child_key), inserter);
-      }
-      break;
-    }
-  }
-}
-
-void TrackEventParser::ParseTaskExecutionArgs(
-    ConstBytes task_execution,
-    PacketSequenceStateGeneration* sequence_state,
-    ArgsTracker::BoundInserter* inserter) {
-  protos::pbzero::TaskExecution::Decoder task(task_execution.data,
-                                              task_execution.size);
-  uint64_t iid = task.posted_from_iid();
-  if (!iid)
-    return;
-
-  auto* decoder = sequence_state->LookupInternedMessage<
-      protos::pbzero::InternedData::kSourceLocationsFieldNumber,
-      protos::pbzero::SourceLocation>(iid);
-  if (!decoder)
-    return;
-
-  StringId file_name_id = kNullStringId;
-  StringId function_name_id = kNullStringId;
-  uint32_t line_number = 0;
-
-  TraceStorage* storage = context_->storage.get();
-  file_name_id = storage->InternString(decoder->file_name());
-  function_name_id = storage->InternString(decoder->function_name());
-  line_number = decoder->line_number();
-
-  inserter->AddArg(task_file_name_args_key_id_, Variadic::String(file_name_id));
-  inserter->AddArg(task_function_name_args_key_id_,
-                   Variadic::String(function_name_id));
-
-  inserter->AddArg(task_line_number_args_key_id_,
-                   Variadic::UnsignedInteger(line_number));
-}
-
-void TrackEventParser::ParseLogMessage(
-    ConstBytes blob,
-    PacketSequenceStateGeneration* sequence_state,
-    int64_t ts,
-    base::Optional<UniqueTid> utid,
-    ArgsTracker::BoundInserter* inserter) {
-  if (!utid) {
-    context_->storage->IncrementStats(stats::track_event_parser_errors);
-    PERFETTO_DLOG("LogMessage without thread association");
-    return;
-  }
-
-  protos::pbzero::LogMessage::Decoder message(blob.data, blob.size);
-
-  TraceStorage* storage = context_->storage.get();
-
-  StringId log_message_id = kNullStringId;
-
-  auto* decoder = sequence_state->LookupInternedMessage<
-      protos::pbzero::InternedData::kLogMessageBodyFieldNumber,
-      protos::pbzero::LogMessageBody>(message.body_iid());
-  if (!decoder)
-    return;
-
-  log_message_id = storage->InternString(decoder->body());
-
-  // TODO(nicomazz): LogMessage also contains the source of the message (file
-  // and line number). Android logs doesn't support this so far.
-  context_->storage->mutable_android_log_table()->Insert(
-      {ts, *utid,
-       /*priority*/ 0,
-       /*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));
-  // TODO(nicomazz): Add the source location as an argument.
-}
-
-void TrackEventParser::ParseCcScheduler(
-    ConstBytes cc,
-    PacketSequenceStateGeneration* sequence_state,
-    ArgsTracker::BoundInserter* outer_inserter) {
-  // The 79 decides the initial amount of memory reserved in the prefix. This
-  // was determined my manually counting the length of the longest column.
-  constexpr size_t kCcSchedulerStateMaxColumnLength = 79;
-  ProtoToArgsTable helper(sequence_state, context_,
-                          /* starting_prefix = */ "",
-                          kCcSchedulerStateMaxColumnLength);
-  auto status = helper.AddProtoFileDescriptor(
-      kChromeCompositorSchedulerStateDescriptor.data(),
-      kChromeCompositorSchedulerStateDescriptor.size());
-  PERFETTO_DCHECK(status.ok());
-
-  // Switch |source_location_iid| into its interned data variant.
-  helper.AddParsingOverride(
-      "begin_impl_frame_args.current_args.source_location_iid",
-      [](const ProtoToArgsTable::ParsingOverrideState& state,
-         const protozero::Field& field, ArgsTracker::BoundInserter* inserter) {
-        return MaybeParseSourceLocation("begin_impl_frame_args.current_args",
-                                        state, field, inserter);
-      });
-  helper.AddParsingOverride(
-      "begin_impl_frame_args.last_args.source_location_iid",
-      [](const ProtoToArgsTable::ParsingOverrideState& state,
-         const protozero::Field& field, ArgsTracker::BoundInserter* inserter) {
-        return MaybeParseSourceLocation("begin_impl_frame_args.last_args",
-                                        state, field, inserter);
-      });
-  helper.AddParsingOverride(
-      "begin_frame_observer_state.last_begin_frame_args.source_location_iid",
-      [](const ProtoToArgsTable::ParsingOverrideState& state,
-         const protozero::Field& field, ArgsTracker::BoundInserter* inserter) {
-        return MaybeParseSourceLocation(
-            "begin_frame_observer_state.last_begin_frame_args", state, field,
-            inserter);
-      });
-  helper.InternProtoIntoArgsTable(
-      cc, ".perfetto.protos.ChromeCompositorSchedulerState", outer_inserter);
-}
-
-void TrackEventParser::ParseChromeUserEvent(
-    protozero::ConstBytes chrome_user_event,
-    ArgsTracker::BoundInserter* inserter) {
-  protos::pbzero::ChromeUserEvent::Decoder event(chrome_user_event.data,
-                                                 chrome_user_event.size);
-  if (event.has_action()) {
-    StringId action_id = context_->storage->InternString(event.action());
-    inserter->AddArg(chrome_user_event_action_args_key_id_,
-                     Variadic::String(action_id));
-  }
-}
-
-void TrackEventParser::ParseChromeLegacyIpc(
-    protozero::ConstBytes chrome_legacy_ipc,
-    ArgsTracker::BoundInserter* inserter) {
-  protos::pbzero::ChromeLegacyIpc::Decoder event(chrome_legacy_ipc.data,
-                                                 chrome_legacy_ipc.size);
-  if (event.has_message_class()) {
-    size_t message_class_index = static_cast<size_t>(event.message_class());
-    if (message_class_index >= chrome_legacy_ipc_class_ids_.size())
-      message_class_index = 0;
-    inserter->AddArg(
-
-        chrome_legacy_ipc_class_args_key_id_,
-        Variadic::String(chrome_legacy_ipc_class_ids_[message_class_index]));
-  }
-  if (event.has_message_line()) {
-    inserter->AddArg(chrome_legacy_ipc_line_args_key_id_,
-                     Variadic::Integer(event.message_line()));
-  }
-}
-
-void TrackEventParser::ParseChromeKeyedService(
-    protozero::ConstBytes chrome_keyed_service,
-    ArgsTracker::BoundInserter* inserter) {
-  protos::pbzero::ChromeKeyedService::Decoder event(chrome_keyed_service.data,
-                                                    chrome_keyed_service.size);
-  if (event.has_name()) {
-    StringId action_id = context_->storage->InternString(event.name());
-    inserter->AddArg(chrome_keyed_service_name_args_key_id_,
-
-                     Variadic::String(action_id));
-  }
-}
-
-void TrackEventParser::ParseChromeHistogramSample(
-    protozero::ConstBytes chrome_histogram_sample,
-    ArgsTracker::BoundInserter* inserter) {
-  protos::pbzero::ChromeHistogramSample::Decoder event(
-      chrome_histogram_sample.data, chrome_histogram_sample.size);
-  if (event.has_name_hash()) {
-    uint64_t name_hash = static_cast<uint64_t>(event.name_hash());
-    inserter->AddArg(chrome_histogram_sample_name_hash_args_key_id_,
-                     Variadic::UnsignedInteger(name_hash));
-  }
-  if (event.has_name()) {
-    StringId name = context_->storage->InternString(event.name());
-    inserter->AddArg(chrome_keyed_service_name_args_key_id_,
-                     Variadic::String(name));
-  }
-  if (event.has_sample()) {
-    int64_t sample = static_cast<int64_t>(event.sample());
-    inserter->AddArg(chrome_histogram_sample_sample_args_key_id_,
-                     Variadic::Integer(sample));
+    PERFETTO_DLOG("%s", status.c_message());
   }
 }
 
diff --git a/src/trace_processor/importers/proto/track_event_parser.h b/src/trace_processor/importers/proto/track_event_parser.h
index 10d8e03..7b5cd79 100644
--- a/src/trace_processor/importers/proto/track_event_parser.h
+++ b/src/trace_processor/importers/proto/track_event_parser.h
@@ -17,19 +17,22 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_TRACK_EVENT_PARSER_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_TRACK_EVENT_PARSER_H_
 
+#include "perfetto/base/build_config.h"
 #include "perfetto/protozero/field.h"
-#include "src/trace_processor/args_tracker.h"
-#include "src/trace_processor/slice_tracker.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/proto/args_table_utils.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/timestamped_trace_piece.h"
+
+#include "protos/perfetto/trace/track_event/track_event.pbzero.h"
+
+namespace Json {
+class Value;
+}
 
 namespace perfetto {
 
-namespace protos {
-namespace pbzero {
-class TrackEvent_LegacyEvent_Decoder;
-}  // namespace pbzero
-}  // namespace protos
-
 namespace trace_processor {
 
 class PacketSequenceStateGeneration;
@@ -44,52 +47,21 @@
   UniqueTid ParseThreadDescriptor(protozero::ConstBytes);
 
   void ParseTrackEvent(int64_t ts,
-                       int64_t tts,
-                       int64_t ticount,
-                       PacketSequenceStateGeneration*,
+                       TrackEventData* event_data,
                        protozero::ConstBytes);
 
  private:
-  void ParseChromeProcessDescriptor(UniquePid upid, protozero::ConstBytes);
-  void ParseChromeThreadDescriptor(UniqueTid utid, protozero::ConstBytes);
-  void ParseLegacyEventAsRawEvent(
-      int64_t ts,
-      int64_t tts,
-      int64_t ticount,
-      base::Optional<UniqueTid> utid,
-      StringId category_id,
-      StringId name_id,
-      const protos::pbzero::TrackEvent_LegacyEvent_Decoder& legacy_event,
-      SliceTracker::SetArgsCallback slice_args_callback);
-  void ParseDebugAnnotationArgs(protozero::ConstBytes debug_annotation,
-                                PacketSequenceStateGeneration*,
-                                ArgsTracker::BoundInserter* inserter);
-  void ParseNestedValueArgs(protozero::ConstBytes nested_value,
-                            base::StringView flat_key,
-                            base::StringView key,
-                            ArgsTracker::BoundInserter* inserter);
-  void ParseTaskExecutionArgs(protozero::ConstBytes task_execution,
-                              PacketSequenceStateGeneration*,
-                              ArgsTracker::BoundInserter* inserter);
-  void ParseLogMessage(protozero::ConstBytes,
-                       PacketSequenceStateGeneration*,
-                       int64_t,
-                       base::Optional<UniqueTid>,
-                       ArgsTracker::BoundInserter* inserter);
-  void ParseCcScheduler(protozero::ConstBytes cc_scheduler,
-                        PacketSequenceStateGeneration*,
-                        ArgsTracker::BoundInserter* inserter);
-  void ParseChromeUserEvent(protozero::ConstBytes chrome_user_event,
-                            ArgsTracker::BoundInserter* inserter);
-  void ParseChromeLegacyIpc(protozero::ConstBytes chrome_legacy_ipc,
-                            ArgsTracker::BoundInserter* inserter);
-  void ParseChromeKeyedService(protozero::ConstBytes chrome_keyed_service,
-                               ArgsTracker::BoundInserter* inserter);
-  void ParseChromeHistogramSample(protozero::ConstBytes chrome_keyed_service,
-                                  ArgsTracker::BoundInserter* inserter);
+  class EventImporter;
+
+  void ParseChromeProcessDescriptor(UniquePid, protozero::ConstBytes);
+  void ParseChromeThreadDescriptor(UniqueTid, protozero::ConstBytes);
+  void ParseCounterDescriptor(TrackId, protozero::ConstBytes);
 
   TraceProcessorContext* context_;
+  ProtoToArgsTable proto_to_args_;
 
+  const StringId counter_name_thread_time_id_;
+  const StringId counter_name_thread_instruction_count_id_;
   const StringId task_file_name_args_key_id_;
   const StringId task_function_name_args_key_id_;
   const StringId task_line_number_args_key_id_;
@@ -116,16 +88,22 @@
   const StringId flow_direction_value_out_id_;
   const StringId flow_direction_value_inout_id_;
   const StringId chrome_user_event_action_args_key_id_;
+  const StringId chrome_user_event_action_hash_args_key_id_;
   const StringId chrome_legacy_ipc_class_args_key_id_;
   const StringId chrome_legacy_ipc_line_args_key_id_;
   const StringId chrome_keyed_service_name_args_key_id_;
   const StringId chrome_histogram_sample_name_hash_args_key_id_;
   const StringId chrome_histogram_sample_name_args_key_id_;
   const StringId chrome_histogram_sample_sample_args_key_id_;
+  const StringId chrome_latency_info_trace_id_key_id_;
+  const StringId chrome_latency_info_step_key_id_;
+  const StringId chrome_latency_info_frame_tree_node_id_key_id_;
 
+  std::array<StringId, 8> chrome_latency_info_step_ids_;
   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_;
+  std::array<StringId, 4> counter_unit_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 ebd1391..772dd6e 100644
--- a/src/trace_processor/importers/proto/track_event_tokenizer.cc
+++ b/src/trace_processor/importers/proto/track_event_tokenizer.cc
@@ -17,21 +17,21 @@
 #include "src/trace_processor/importers/proto/track_event_tokenizer.h"
 
 #include "perfetto/base/logging.h"
-#include "perfetto/protozero/proto_decoder.h"
-#include "src/trace_processor/clock_tracker.h"
+#include "src/trace_processor/importers/common/clock_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
 #include "src/trace_processor/importers/proto/packet_sequence_state.h"
 #include "src/trace_processor/importers/proto/proto_trace_tokenizer.h"
-#include "src/trace_processor/process_tracker.h"
-#include "src/trace_processor/stats.h"
+#include "src/trace_processor/storage/stats.h"
+#include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/trace_sorter.h"
-#include "src/trace_processor/trace_storage.h"
-#include "src/trace_processor/track_tracker.h"
 
 #include "protos/perfetto/trace/clock_snapshot.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.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/counter_descriptor.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"
@@ -45,61 +45,73 @@
 
 ModuleResult TrackEventTokenizer::TokenizeTrackDescriptorPacket(
     PacketSequenceState* state,
-    const protos::pbzero::TracePacket::Decoder& packet_decoder,
+    const protos::pbzero::TracePacket::Decoder& packet,
     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);
+  auto track_descriptor_field = packet.track_descriptor();
+  protos::pbzero::TrackDescriptor::Decoder track(track_descriptor_field.data,
+                                                 track_descriptor_field.size);
 
-  if (!track_descriptor_decoder.has_uuid()) {
+  if (!track.has_uuid()) {
     PERFETTO_ELOG("TrackDescriptor packet without uuid");
     context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
     return ModuleResult::Handled();
   }
 
-  if (track_descriptor_decoder.has_thread()) {
-    auto thread_descriptor_field = track_descriptor_decoder.thread();
-    protos::pbzero::ThreadDescriptor::Decoder thread_descriptor_decoder(
-        thread_descriptor_field.data, thread_descriptor_field.size);
+  if (track.has_thread()) {
+    protos::pbzero::ThreadDescriptor::Decoder thread(track.thread());
 
-    if (!thread_descriptor_decoder.has_pid() ||
-        !thread_descriptor_decoder.has_tid()) {
+    if (!thread.has_pid() || !thread.has_tid()) {
       PERFETTO_ELOG(
           "No pid or tid in ThreadDescriptor for track with uuid %" PRIu64,
-          track_descriptor_decoder.uuid());
+          track.uuid());
       context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
       return ModuleResult::Handled();
     }
 
     if (state->IsIncrementalStateValid()) {
-      TokenizeThreadDescriptor(state, thread_descriptor_decoder);
+      TokenizeThreadDescriptor(state, thread);
     }
 
     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);
+        track.uuid(), track.parent_uuid(), static_cast<uint32_t>(thread.pid()),
+        static_cast<uint32_t>(thread.tid()), packet_timestamp);
+  } else if (track.has_process()) {
+    protos::pbzero::ProcessDescriptor::Decoder process(track.process());
 
-    if (!process_descriptor_decoder.has_pid()) {
+    if (!process.has_pid()) {
       PERFETTO_ELOG("No pid in ProcessDescriptor for track with uuid %" PRIu64,
-                    track_descriptor_decoder.uuid());
+                    track.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);
+        track.uuid(), static_cast<uint32_t>(process.pid()), packet_timestamp);
+  } else if (track.has_counter()) {
+    protos::pbzero::CounterDescriptor::Decoder counter(track.counter());
+
+    StringId category_id = kNullStringId;
+    if (counter.has_categories()) {
+      // TODO(eseckler): Support multi-category events in the table schema.
+      std::string categories;
+      for (auto it = counter.categories(); it; ++it) {
+        if (!categories.empty())
+          categories += ",";
+        categories.append((*it).data, (*it).size);
+      }
+      if (!categories.empty()) {
+        category_id =
+            context_->storage->InternString(base::StringView(categories));
+      }
+    }
+
+    context_->track_tracker->ReserveDescriptorCounterTrack(
+        track.uuid(), track.parent_uuid(), category_id,
+        counter.unit_multiplier(), counter.is_incremental(),
+        packet.trusted_packet_sequence_id());
   } else {
-    context_->track_tracker->ReserveDescriptorChildTrack(
-        track_descriptor_decoder.uuid(),
-        track_descriptor_decoder.parent_uuid());
+    context_->track_tracker->ReserveDescriptorChildTrack(track.uuid(),
+                                                         track.parent_uuid());
   }
 
   // Let ProtoTraceTokenizer forward the packet to the parser.
@@ -108,8 +120,8 @@
 
 ModuleResult TrackEventTokenizer::TokenizeThreadDescriptorPacket(
     PacketSequenceState* state,
-    const protos::pbzero::TracePacket::Decoder& packet_decoder) {
-  if (PERFETTO_UNLIKELY(!packet_decoder.has_trusted_packet_sequence_id())) {
+    const protos::pbzero::TracePacket::Decoder& packet) {
+  if (PERFETTO_UNLIKELY(!packet.has_trusted_packet_sequence_id())) {
     PERFETTO_ELOG("ThreadDescriptor packet without trusted_packet_sequence_id");
     context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
     return ModuleResult::Handled();
@@ -125,10 +137,8 @@
     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);
+  protos::pbzero::ThreadDescriptor::Decoder thread(packet.thread_descriptor());
+  TokenizeThreadDescriptor(state, thread);
 
   // Let ProtoTraceTokenizer forward the packet to the parser.
   return ModuleResult::Ignored();
@@ -136,62 +146,41 @@
 
 void TrackEventTokenizer::TokenizeThreadDescriptor(
     PacketSequenceState* state,
-    const protos::pbzero::ThreadDescriptor::Decoder&
-        thread_descriptor_decoder) {
+    const protos::pbzero::ThreadDescriptor::Decoder& thread) {
   // 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());
+  state->SetThreadDescriptor(thread.pid(), thread.tid(),
+                             thread.reference_timestamp_us() * 1000,
+                             thread.reference_thread_time_us() * 1000,
+                             thread.reference_thread_instruction_count());
 }
 
 void TrackEventTokenizer::TokenizeTrackEventPacket(
     PacketSequenceState* state,
-    const protos::pbzero::TracePacket::Decoder& packet_decoder,
-    TraceBlobView* packet,
+    const protos::pbzero::TracePacket::Decoder& packet,
+    TraceBlobView* packet_blob,
     int64_t packet_timestamp) {
-  constexpr auto kTimestampDeltaUsFieldNumber =
-      protos::pbzero::TrackEvent::kTimestampDeltaUsFieldNumber;
-  constexpr auto kTimestampAbsoluteUsFieldNumber =
-      protos::pbzero::TrackEvent::kTimestampAbsoluteUsFieldNumber;
-  constexpr auto kThreadTimeDeltaUsFieldNumber =
-      protos::pbzero::TrackEvent::kThreadTimeDeltaUsFieldNumber;
-  constexpr auto kThreadTimeAbsoluteUsFieldNumber =
-      protos::pbzero::TrackEvent::kThreadTimeAbsoluteUsFieldNumber;
-  constexpr auto kThreadInstructionCountDeltaFieldNumber =
-      protos::pbzero::TrackEvent::kThreadInstructionCountDeltaFieldNumber;
-  constexpr auto kThreadInstructionCountAbsoluteFieldNumber =
-      protos::pbzero::TrackEvent::kThreadInstructionCountAbsoluteFieldNumber;
-
-  if (PERFETTO_UNLIKELY(!packet_decoder.has_trusted_packet_sequence_id())) {
+  if (PERFETTO_UNLIKELY(!packet.has_trusted_packet_sequence_id())) {
     PERFETTO_ELOG("TrackEvent packet without trusted_packet_sequence_id");
     context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
     return;
   }
 
-  // TODO(eseckler): For now, TrackEvents can only be parsed correctly while
-  // incremental state for their sequence is valid, because chromium doesn't set
-  // SEQ_NEEDS_INCREMENTAL_STATE yet. Remove this once it does.
-  if (!state->IsIncrementalStateValid()) {
-    context_->storage->IncrementStats(stats::tokenizer_skipped_packets);
-    return;
-  }
+  auto field = packet.track_event();
+  protos::pbzero::TrackEvent::Decoder event(field.data, field.size);
 
-  auto field = packet_decoder.track_event();
-  protozero::ProtoDecoder event_decoder(field.data, field.size);
+  protos::pbzero::TrackEventDefaults::Decoder* defaults =
+      state->current_generation()->GetTrackEventDefaults();
 
   int64_t timestamp;
-  int64_t thread_timestamp = 0;
-  int64_t thread_instructions = 0;
+  std::unique_ptr<TrackEventData> data(
+      new TrackEventData(std::move(*packet_blob), state->current_generation()));
 
   // TODO(eseckler): Remove handling of timestamps relative to ThreadDescriptors
   // once all producers have switched to clock-domain timestamps (e.g.
   // TracePacket's timestamp).
 
-  if (auto ts_delta_field =
-          event_decoder.FindField(kTimestampDeltaUsFieldNumber)) {
+  if (event.has_timestamp_delta_us()) {
     // Delta timestamps require a valid ThreadDescriptor packet since the last
     // packet loss.
     if (!state->track_event_timestamps_valid()) {
@@ -199,7 +188,7 @@
       return;
     }
     timestamp = state->IncrementAndGetTrackEventTimeNs(
-        ts_delta_field.as_int64() * 1000);
+        event.timestamp_delta_us() * 1000);
 
     // Legacy TrackEvent timestamp fields are in MONOTONIC domain. Adjust to
     // trace time if we have a clock snapshot.
@@ -207,9 +196,7 @@
         protos::pbzero::ClockSnapshot::Clock::MONOTONIC, timestamp);
     if (trace_ts.has_value())
       timestamp = trace_ts.value();
-  } else if (int64_t ts_absolute_us =
-                 event_decoder.FindField(kTimestampAbsoluteUsFieldNumber)
-                     .as_int64()) {
+  } else if (int64_t ts_absolute_us = event.timestamp_absolute_us()) {
     // One-off absolute timestamps don't affect delta computation.
     timestamp = ts_absolute_us * 1000;
 
@@ -219,7 +206,7 @@
         protos::pbzero::ClockSnapshot::Clock::MONOTONIC, timestamp);
     if (trace_ts.has_value())
       timestamp = trace_ts.value();
-  } else if (packet_decoder.has_timestamp()) {
+  } else if (packet.has_timestamp()) {
     timestamp = packet_timestamp;
   } else {
     PERFETTO_ELOG("TrackEvent without valid timestamp");
@@ -227,42 +214,121 @@
     return;
   }
 
-  if (auto tt_delta_field =
-          event_decoder.FindField(kThreadTimeDeltaUsFieldNumber)) {
+  if (event.has_thread_time_delta_us()) {
     // Delta timestamps require a valid ThreadDescriptor packet since the last
     // packet loss.
     if (!state->track_event_timestamps_valid()) {
       context_->storage->IncrementStats(stats::tokenizer_skipped_packets);
       return;
     }
-    thread_timestamp = state->IncrementAndGetTrackEventThreadTimeNs(
-        tt_delta_field.as_int64() * 1000);
-  } else if (auto tt_absolute_field =
-                 event_decoder.FindField(kThreadTimeAbsoluteUsFieldNumber)) {
+    data->thread_timestamp = state->IncrementAndGetTrackEventThreadTimeNs(
+        event.thread_time_delta_us() * 1000);
+  } else if (event.has_thread_time_absolute_us()) {
     // One-off absolute timestamps don't affect delta computation.
-    thread_timestamp = tt_absolute_field.as_int64() * 1000;
+    data->thread_timestamp = event.thread_time_absolute_us() * 1000;
   }
 
-  if (auto ti_delta_field =
-          event_decoder.FindField(kThreadInstructionCountDeltaFieldNumber)) {
+  if (event.has_thread_instruction_count_delta()) {
     // Delta timestamps require a valid ThreadDescriptor packet since the last
     // packet loss.
     if (!state->track_event_timestamps_valid()) {
       context_->storage->IncrementStats(stats::tokenizer_skipped_packets);
       return;
     }
-    thread_instructions =
+    data->thread_instruction_count =
         state->IncrementAndGetTrackEventThreadInstructionCount(
-            ti_delta_field.as_int64());
-  } else if (auto ti_absolute_field = event_decoder.FindField(
-                 kThreadInstructionCountAbsoluteFieldNumber)) {
+            event.thread_instruction_count_delta());
+  } else if (event.has_thread_instruction_count_absolute()) {
     // One-off absolute timestamps don't affect delta computation.
-    thread_instructions = ti_absolute_field.as_int64();
+    data->thread_instruction_count = event.thread_instruction_count_absolute();
   }
 
-  context_->sorter->PushTrackEventPacket(timestamp, thread_timestamp,
-                                         thread_instructions, state,
-                                         std::move(*packet));
+  // TODO(eseckler): Also convert & attach counter values from TYPE_COUNTER
+  // events and extra_counter_* fields.
+  if (event.type() == protos::pbzero::TrackEvent::TYPE_COUNTER) {
+    // Consider track_uuid from the packet and TrackEventDefaults.
+    uint64_t track_uuid;
+    if (event.has_track_uuid()) {
+      track_uuid = event.track_uuid();
+    } else if (defaults->has_track_uuid()) {
+      track_uuid = defaults->track_uuid();
+    } else {
+      PERFETTO_DLOG(
+          "Ignoring TrackEvent with counter_value but without track_uuid");
+      context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
+      return;
+    }
+
+    if (!event.has_counter_value()) {
+      PERFETTO_DLOG(
+          "Ignoring TrackEvent with TYPE_COUNTER but without counter_value for "
+          "track_uuid %" PRIu64,
+          track_uuid);
+      context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
+      return;
+    }
+
+    base::Optional<int64_t> value =
+        context_->track_tracker->ConvertToAbsoluteCounterValue(
+            track_uuid, packet.trusted_packet_sequence_id(),
+            event.counter_value());
+
+    if (!value) {
+      PERFETTO_DLOG("Ignoring TrackEvent with invalid track_uuid %" PRIu64,
+                    track_uuid);
+      context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
+      return;
+    }
+
+    data->counter_value = *value;
+  }
+
+  if (event.has_extra_counter_values()) {
+    // Consider extra_counter_track_uuids from the packet and
+    // TrackEventDefaults.
+    protozero::RepeatedFieldIterator<uint64_t> track_uuid_it;
+    if (event.has_extra_counter_track_uuids()) {
+      track_uuid_it = event.extra_counter_track_uuids();
+    } else if (defaults->has_extra_counter_track_uuids()) {
+      track_uuid_it = defaults->extra_counter_track_uuids();
+    } else {
+      PERFETTO_DLOG(
+          "Ignoring TrackEvent with extra_counter_values but without "
+          "extra_counter_track_uuids");
+      context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
+      return;
+    }
+
+    size_t index = 0;
+    for (auto value_it = event.extra_counter_values(); value_it;
+         ++value_it, ++track_uuid_it, ++index) {
+      if (!track_uuid_it) {
+        PERFETTO_DLOG(
+            "Ignoring TrackEvent with more extra_counter_values than "
+            "extra_counter_track_uuids");
+        context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
+        return;
+      }
+      if (index >= TrackEventData::kMaxNumExtraCounters) {
+        PERFETTO_ELOG(
+            "Ignoring TrackEvent with more extra_counter_values than "
+            "TrackEventData::kMaxNumExtraCounters");
+        context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
+        return;
+      }
+      base::Optional<int64_t> value =
+          context_->track_tracker->ConvertToAbsoluteCounterValue(
+              *track_uuid_it, packet.trusted_packet_sequence_id(), *value_it);
+      if (!value) {
+        PERFETTO_DLOG("Ignoring TrackEvent with invalid extra counter track");
+        context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
+        return;
+      }
+      data->extra_counter_values[index] = *value;
+    }
+  }
+
+  context_->sorter->PushTrackEventPacket(timestamp, std::move(data));
 }
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/track_event_tokenizer.h b/src/trace_processor/importers/proto/track_event_tokenizer.h
index 48fdf47..0f914db 100644
--- a/src/trace_processor/importers/proto/track_event_tokenizer.h
+++ b/src/trace_processor/importers/proto/track_event_tokenizer.h
@@ -20,7 +20,7 @@
 #include <stdint.h>
 
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/trace_storage.h"
 
 namespace perfetto {
 
diff --git a/src/trace_processor/importers/proto/vulkan_memory_tracker.cc b/src/trace_processor/importers/proto/vulkan_memory_tracker.cc
index 03d1776..7090943 100644
--- a/src/trace_processor/importers/proto/vulkan_memory_tracker.cc
+++ b/src/trace_processor/importers/proto/vulkan_memory_tracker.cc
@@ -18,8 +18,8 @@
 
 #include <string>
 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
-#include "src/trace_processor/process_tracker.h"
-#include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 #include "perfetto/base/logging.h"
 
diff --git a/src/trace_processor/importers/proto/vulkan_memory_tracker.h b/src/trace_processor/importers/proto/vulkan_memory_tracker.h
index e437717..1457923 100644
--- a/src/trace_processor/importers/proto/vulkan_memory_tracker.h
+++ b/src/trace_processor/importers/proto/vulkan_memory_tracker.h
@@ -18,7 +18,7 @@
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_VULKAN_MEMORY_TRACKER_H_
 
 #include "src/trace_processor/importers/proto/proto_incremental_state.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/trace_storage.h"
 
 #include "protos/perfetto/trace/gpu/vulkan_memory_event.pbzero.h"
 
diff --git a/src/trace_processor/syscall_tracker.cc b/src/trace_processor/importers/syscalls/syscall_tracker.cc
similarity index 83%
rename from src/trace_processor/syscall_tracker.cc
rename to src/trace_processor/importers/syscalls/syscall_tracker.cc
index 5991497..129502d 100644
--- a/src/trace_processor/syscall_tracker.cc
+++ b/src/trace_processor/importers/syscalls/syscall_tracker.cc
@@ -14,19 +14,20 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/syscall_tracker.h"
+#include "src/trace_processor/importers/syscalls/syscall_tracker.h"
 
 #include <type_traits>
 #include <utility>
 
 #include <inttypes.h>
 
-#include "src/trace_processor/stats.h"
+#include "src/trace_processor/storage/stats.h"
 
-#include "src/trace_processor/syscalls_aarch32.h"
-#include "src/trace_processor/syscalls_aarch64.h"
-#include "src/trace_processor/syscalls_armeabi.h"
-#include "src/trace_processor/syscalls_x86_64.h"
+#include "src/trace_processor/importers/syscalls/syscalls_aarch32.h"
+#include "src/trace_processor/importers/syscalls/syscalls_aarch64.h"
+#include "src/trace_processor/importers/syscalls/syscalls_armeabi.h"
+#include "src/trace_processor/importers/syscalls/syscalls_x86.h"
+#include "src/trace_processor/importers/syscalls/syscalls_x86_64.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -76,6 +77,10 @@
       num_syscalls = GetSyscalls(kSyscalls_x86_64);
       syscall_table = &kSyscalls_x86_64[0];
       break;
+    case kX86:
+      num_syscalls = GetSyscalls(kSyscalls_x86);
+      syscall_table = &kSyscalls_x86[0];
+      break;
     case kUnknown:
       num_syscalls = 0;
       syscall_table = &kSyscalls_Unknown[0];
diff --git a/src/trace_processor/syscall_tracker.h b/src/trace_processor/importers/syscalls/syscall_tracker.h
similarity index 86%
rename from src/trace_processor/syscall_tracker.h
rename to src/trace_processor/importers/syscalls/syscall_tracker.h
index 88d67b4..c31ffe8 100644
--- a/src/trace_processor/syscall_tracker.h
+++ b/src/trace_processor/importers/syscalls/syscall_tracker.h
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_SYSCALL_TRACKER_H_
-#define SRC_TRACE_PROCESSOR_SYSCALL_TRACKER_H_
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_SYSCALLS_SYSCALL_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_SYSCALLS_SYSCALL_TRACKER_H_
 
 #include <limits>
 #include <tuple>
 
 #include "perfetto/ext/base/string_view.h"
-#include "src/trace_processor/destructible.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 "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/destructible.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -38,6 +38,7 @@
   kAarch32,  // 64-bit kernel running a 32-bit process (should be rare).
   kAarch64,  // 64-bit kernel running a 64-bit process (most new devices).
   kX86_64,
+  kX86,
 };
 
 class SyscallTracker : public Destructible {
@@ -98,4 +99,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_SYSCALL_TRACKER_H_
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_SYSCALLS_SYSCALL_TRACKER_H_
diff --git a/src/trace_processor/syscall_tracker_unittest.cc b/src/trace_processor/importers/syscalls/syscall_tracker_unittest.cc
similarity index 97%
rename from src/trace_processor/syscall_tracker_unittest.cc
rename to src/trace_processor/importers/syscalls/syscall_tracker_unittest.cc
index 61124d1..17733eb 100644
--- a/src/trace_processor/syscall_tracker_unittest.cc
+++ b/src/trace_processor/importers/syscalls/syscall_tracker_unittest.cc
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/syscall_tracker.h"
+#include "src/trace_processor/importers/syscalls/syscall_tracker.h"
 
-#include "src/trace_processor/slice_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
 #include "test/gtest_and_gmock.h"
 
 namespace perfetto {
diff --git a/src/trace_processor/syscalls_aarch32.h b/src/trace_processor/importers/syscalls/syscalls_aarch32.h
similarity index 98%
rename from src/trace_processor/syscalls_aarch32.h
rename to src/trace_processor/importers/syscalls/syscalls_aarch32.h
index 9317278..e6a5475 100644
--- a/src/trace_processor/syscalls_aarch32.h
+++ b/src/trace_processor/importers/syscalls/syscalls_aarch32.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_SYSCALLS_AARCH32_H_
-#define SRC_TRACE_PROCESSOR_SYSCALLS_AARCH32_H_
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_SYSCALLS_SYSCALLS_AARCH32_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_SYSCALLS_SYSCALLS_AARCH32_H_
 
 namespace perfetto {
 namespace trace_processor {
@@ -426,4 +426,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_SYSCALLS_AARCH32_H_
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_SYSCALLS_SYSCALLS_AARCH32_H_
diff --git a/src/trace_processor/syscalls_aarch64.h b/src/trace_processor/importers/syscalls/syscalls_aarch64.h
similarity index 98%
rename from src/trace_processor/syscalls_aarch64.h
rename to src/trace_processor/importers/syscalls/syscalls_aarch64.h
index 007da59..67a82b6 100644
--- a/src/trace_processor/syscalls_aarch64.h
+++ b/src/trace_processor/importers/syscalls/syscalls_aarch64.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_SYSCALLS_AARCH64_H_
-#define SRC_TRACE_PROCESSOR_SYSCALLS_AARCH64_H_
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_SYSCALLS_SYSCALLS_AARCH64_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_SYSCALLS_SYSCALLS_AARCH64_H_
 
 namespace perfetto {
 namespace trace_processor {
@@ -321,4 +321,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_SYSCALLS_AARCH64_H_
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_SYSCALLS_SYSCALLS_AARCH64_H_
diff --git a/src/trace_processor/syscalls_armeabi.h b/src/trace_processor/importers/syscalls/syscalls_armeabi.h
similarity index 98%
rename from src/trace_processor/syscalls_armeabi.h
rename to src/trace_processor/importers/syscalls/syscalls_armeabi.h
index 312b33f..eed478e 100644
--- a/src/trace_processor/syscalls_armeabi.h
+++ b/src/trace_processor/importers/syscalls/syscalls_armeabi.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_SYSCALLS_ARMEABI_H_
-#define SRC_TRACE_PROCESSOR_SYSCALLS_ARMEABI_H_
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_SYSCALLS_SYSCALLS_ARMEABI_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_SYSCALLS_SYSCALLS_ARMEABI_H_
 
 namespace perfetto {
 namespace trace_processor {
@@ -427,4 +427,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_SYSCALLS_ARMEABI_H_
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_SYSCALLS_SYSCALLS_ARMEABI_H_
diff --git a/src/trace_processor/importers/syscalls/syscalls_x86.h b/src/trace_processor/importers/syscalls/syscalls_x86.h
new file mode 100644
index 0000000..ddd136c
--- /dev/null
+++ b/src/trace_processor/importers/syscalls/syscalls_x86.h
@@ -0,0 +1,417 @@
+/*
+ * 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_IMPORTERS_SYSCALLS_SYSCALLS_X86_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_SYSCALLS_SYSCALLS_X86_H_
+
+namespace perfetto {
+namespace trace_processor {
+
+// See tools/extract_linux_syscall_tables .
+constexpr const char* kSyscalls_x86[] = {
+    "sys_restart_syscall",         // 0
+    "sys_exit",                    // 1
+    "sys_fork",                    // 2
+    "sys_read",                    // 3
+    "sys_write",                   // 4
+    "sys_open",                    // 5
+    "sys_close",                   // 6
+    "sys_waitpid",                 // 7
+    "sys_creat",                   // 8
+    "sys_link",                    // 9
+    "sys_unlink",                  // 10
+    "sys_execve",                  // 11
+    "sys_chdir",                   // 12
+    "sys_time",                    // 13
+    "sys_mknod",                   // 14
+    "sys_chmod",                   // 15
+    "sys_lchown",                  // 16
+    "sys_break",                   // 17
+    "sys_oldstat",                 // 18
+    "sys_lseek",                   // 19
+    "sys_getpid",                  // 20
+    "sys_mount",                   // 21
+    "sys_umount",                  // 22
+    "sys_setuid",                  // 23
+    "sys_getuid",                  // 24
+    "sys_stime",                   // 25
+    "sys_ptrace",                  // 26
+    "sys_alarm",                   // 27
+    "sys_oldfstat",                // 28
+    "sys_pause",                   // 29
+    "sys_utime",                   // 30
+    "sys_stty",                    // 31
+    "sys_gtty",                    // 32
+    "sys_access",                  // 33
+    "sys_nice",                    // 34
+    "sys_ftime",                   // 35
+    "sys_sync",                    // 36
+    "sys_kill",                    // 37
+    "sys_rename",                  // 38
+    "sys_mkdir",                   // 39
+    "sys_rmdir",                   // 40
+    "sys_dup",                     // 41
+    "sys_pipe",                    // 42
+    "sys_times",                   // 43
+    "sys_prof",                    // 44
+    "sys_brk",                     // 45
+    "sys_setgid",                  // 46
+    "sys_getgid",                  // 47
+    "sys_signal",                  // 48
+    "sys_geteuid",                 // 49
+    "sys_getegid",                 // 50
+    "sys_acct",                    // 51
+    "sys_umount2",                 // 52
+    "sys_lock",                    // 53
+    "sys_ioctl",                   // 54
+    "sys_fcntl",                   // 55
+    "sys_mpx",                     // 56
+    "sys_setpgid",                 // 57
+    "sys_ulimit",                  // 58
+    "sys_oldolduname",             // 59
+    "sys_umask",                   // 60
+    "sys_chroot",                  // 61
+    "sys_ustat",                   // 62
+    "sys_dup2",                    // 63
+    "sys_getppid",                 // 64
+    "sys_getpgrp",                 // 65
+    "sys_setsid",                  // 66
+    "sys_sigaction",               // 67
+    "sys_sgetmask",                // 68
+    "sys_ssetmask",                // 69
+    "sys_setreuid",                // 70
+    "sys_setregid",                // 71
+    "sys_sigsuspend",              // 72
+    "sys_sigpending",              // 73
+    "sys_sethostname",             // 74
+    "sys_setrlimit",               // 75
+    "sys_getrlimit",               // 76
+    "sys_getrusage",               // 77
+    "sys_gettimeofday",            // 78
+    "sys_settimeofday",            // 79
+    "sys_getgroups",               // 80
+    "sys_setgroups",               // 81
+    "sys_select",                  // 82
+    "sys_symlink",                 // 83
+    "sys_oldlstat",                // 84
+    "sys_readlink",                // 85
+    "sys_uselib",                  // 86
+    "sys_swapon",                  // 87
+    "sys_reboot",                  // 88
+    "sys_readdir",                 // 89
+    "sys_mmap",                    // 90
+    "sys_munmap",                  // 91
+    "sys_truncate",                // 92
+    "sys_ftruncate",               // 93
+    "sys_fchmod",                  // 94
+    "sys_fchown",                  // 95
+    "sys_getpriority",             // 96
+    "sys_setpriority",             // 97
+    "sys_profil",                  // 98
+    "sys_statfs",                  // 99
+    "sys_fstatfs",                 // 100
+    "sys_ioperm",                  // 101
+    "sys_socketcall",              // 102
+    "sys_syslog",                  // 103
+    "sys_setitimer",               // 104
+    "sys_getitimer",               // 105
+    "sys_stat",                    // 106
+    "sys_lstat",                   // 107
+    "sys_fstat",                   // 108
+    "sys_olduname",                // 109
+    "sys_iopl",                    // 110
+    "sys_vhangup",                 // 111
+    "sys_idle",                    // 112
+    "sys_vm86old",                 // 113
+    "sys_wait4",                   // 114
+    "sys_swapoff",                 // 115
+    "sys_sysinfo",                 // 116
+    "sys_ipc",                     // 117
+    "sys_fsync",                   // 118
+    "sys_sigreturn",               // 119
+    "sys_clone",                   // 120
+    "sys_setdomainname",           // 121
+    "sys_uname",                   // 122
+    "sys_modify_ldt",              // 123
+    "sys_adjtimex",                // 124
+    "sys_mprotect",                // 125
+    "sys_sigprocmask",             // 126
+    "sys_create_module",           // 127
+    "sys_init_module",             // 128
+    "sys_delete_module",           // 129
+    "sys_get_kernel_syms",         // 130
+    "sys_quotactl",                // 131
+    "sys_getpgid",                 // 132
+    "sys_fchdir",                  // 133
+    "sys_bdflush",                 // 134
+    "sys_sysfs",                   // 135
+    "sys_personality",             // 136
+    "sys_afs_syscall",             // 137
+    "sys_setfsuid",                // 138
+    "sys_setfsgid",                // 139
+    "sys__llseek",                 // 140
+    "sys_getdents",                // 141
+    "sys__newselect",              // 142
+    "sys_flock",                   // 143
+    "sys_msync",                   // 144
+    "sys_readv",                   // 145
+    "sys_writev",                  // 146
+    "sys_getsid",                  // 147
+    "sys_fdatasync",               // 148
+    "sys__sysctl",                 // 149
+    "sys_mlock",                   // 150
+    "sys_munlock",                 // 151
+    "sys_mlockall",                // 152
+    "sys_munlockall",              // 153
+    "sys_sched_setparam",          // 154
+    "sys_sched_getparam",          // 155
+    "sys_sched_setscheduler",      // 156
+    "sys_sched_getscheduler",      // 157
+    "sys_sched_yield",             // 158
+    "sys_sched_get_priority_max",  // 159
+    "sys_sched_get_priority_min",  // 160
+    "sys_sched_rr_get_interval",   // 161
+    "sys_nanosleep",               // 162
+    "sys_mremap",                  // 163
+    "sys_setresuid",               // 164
+    "sys_getresuid",               // 165
+    "sys_vm86",                    // 166
+    "sys_query_module",            // 167
+    "sys_poll",                    // 168
+    "sys_nfsservctl",              // 169
+    "sys_setresgid",               // 170
+    "sys_getresgid",               // 171
+    "sys_prctl",                   // 172
+    "sys_rt_sigreturn",            // 173
+    "sys_rt_sigaction",            // 174
+    "sys_rt_sigprocmask",          // 175
+    "sys_rt_sigpending",           // 176
+    "sys_rt_sigtimedwait",         // 177
+    "sys_rt_sigqueueinfo",         // 178
+    "sys_rt_sigsuspend",           // 179
+    "sys_pread64",                 // 180
+    "sys_pwrite64",                // 181
+    "sys_chown",                   // 182
+    "sys_getcwd",                  // 183
+    "sys_capget",                  // 184
+    "sys_capset",                  // 185
+    "sys_sigaltstack",             // 186
+    "sys_sendfile",                // 187
+    "sys_getpmsg",                 // 188
+    "sys_putpmsg",                 // 189
+    "sys_vfork",                   // 190
+    "sys_ugetrlimit",              // 191
+    "sys_mmap2",                   // 192
+    "sys_truncate64",              // 193
+    "sys_ftruncate64",             // 194
+    "sys_stat64",                  // 195
+    "sys_lstat64",                 // 196
+    "sys_fstat64",                 // 197
+    "sys_lchown32",                // 198
+    "sys_getuid32",                // 199
+    "sys_getgid32",                // 200
+    "sys_geteuid32",               // 201
+    "sys_getegid32",               // 202
+    "sys_setreuid32",              // 203
+    "sys_setregid32",              // 204
+    "sys_getgroups32",             // 205
+    "sys_setgroups32",             // 206
+    "sys_fchown32",                // 207
+    "sys_setresuid32",             // 208
+    "sys_getresuid32",             // 209
+    "sys_setresgid32",             // 210
+    "sys_getresgid32",             // 211
+    "sys_chown32",                 // 212
+    "sys_setuid32",                // 213
+    "sys_setgid32",                // 214
+    "sys_setfsuid32",              // 215
+    "sys_setfsgid32",              // 216
+    "sys_pivot_root",              // 217
+    "sys_mincore",                 // 218
+    "sys_madvise",                 // 219
+    "sys_getdents64",              // 220
+    "sys_fcntl64",                 // 221
+    "",                            // 222
+    "",                            // 223
+    "sys_gettid",                  // 224
+    "sys_readahead",               // 225
+    "sys_setxattr",                // 226
+    "sys_lsetxattr",               // 227
+    "sys_fsetxattr",               // 228
+    "sys_getxattr",                // 229
+    "sys_lgetxattr",               // 230
+    "sys_fgetxattr",               // 231
+    "sys_listxattr",               // 232
+    "sys_llistxattr",              // 233
+    "sys_flistxattr",              // 234
+    "sys_removexattr",             // 235
+    "sys_lremovexattr",            // 236
+    "sys_fremovexattr",            // 237
+    "sys_tkill",                   // 238
+    "sys_sendfile64",              // 239
+    "sys_futex",                   // 240
+    "sys_sched_setaffinity",       // 241
+    "sys_sched_getaffinity",       // 242
+    "sys_set_thread_area",         // 243
+    "sys_get_thread_area",         // 244
+    "sys_io_setup",                // 245
+    "sys_io_destroy",              // 246
+    "sys_io_getevents",            // 247
+    "sys_io_submit",               // 248
+    "sys_io_cancel",               // 249
+    "sys_fadvise64",               // 250
+    "",                            // 251
+    "sys_exit_group",              // 252
+    "sys_lookup_dcookie",          // 253
+    "sys_epoll_create",            // 254
+    "sys_epoll_ctl",               // 255
+    "sys_epoll_wait",              // 256
+    "sys_remap_file_pages",        // 257
+    "sys_set_tid_address",         // 258
+    "sys_timer_create",            // 259
+    "sys_timer_settime",           // 260
+    "sys_timer_gettime",           // 261
+    "sys_timer_getoverrun",        // 262
+    "sys_timer_delete",            // 263
+    "sys_clock_settime",           // 264
+    "sys_clock_gettime",           // 265
+    "sys_clock_getres",            // 266
+    "sys_clock_nanosleep",         // 267
+    "sys_statfs64",                // 268
+    "sys_fstatfs64",               // 269
+    "sys_tgkill",                  // 270
+    "sys_utimes",                  // 271
+    "sys_fadvise64_64",            // 272
+    "sys_vserver",                 // 273
+    "sys_mbind",                   // 274
+    "sys_get_mempolicy",           // 275
+    "sys_set_mempolicy",           // 276
+    "sys_mq_open",                 // 277
+    "sys_mq_unlink",               // 278
+    "sys_mq_timedsend",            // 279
+    "sys_mq_timedreceive",         // 280
+    "sys_mq_notify",               // 281
+    "sys_mq_getsetattr",           // 282
+    "sys_kexec_load",              // 283
+    "sys_waitid",                  // 284
+    "",                            // 285
+    "sys_add_key",                 // 286
+    "sys_request_key",             // 287
+    "sys_keyctl",                  // 288
+    "sys_ioprio_set",              // 289
+    "sys_ioprio_get",              // 290
+    "sys_inotify_init",            // 291
+    "sys_inotify_add_watch",       // 292
+    "sys_inotify_rm_watch",        // 293
+    "sys_migrate_pages",           // 294
+    "sys_openat",                  // 295
+    "sys_mkdirat",                 // 296
+    "sys_mknodat",                 // 297
+    "sys_fchownat",                // 298
+    "sys_futimesat",               // 299
+    "sys_fstatat64",               // 300
+    "sys_unlinkat",                // 301
+    "sys_renameat",                // 302
+    "sys_linkat",                  // 303
+    "sys_symlinkat",               // 304
+    "sys_readlinkat",              // 305
+    "sys_fchmodat",                // 306
+    "sys_faccessat",               // 307
+    "sys_pselect6",                // 308
+    "sys_ppoll",                   // 309
+    "sys_unshare",                 // 310
+    "sys_set_robust_list",         // 311
+    "sys_get_robust_list",         // 312
+    "sys_splice",                  // 313
+    "sys_sync_file_range",         // 314
+    "sys_tee",                     // 315
+    "sys_vmsplice",                // 316
+    "sys_move_pages",              // 317
+    "sys_getcpu",                  // 318
+    "sys_epoll_pwait",             // 319
+    "sys_utimensat",               // 320
+    "sys_signalfd",                // 321
+    "sys_timerfd_create",          // 322
+    "sys_eventfd",                 // 323
+    "sys_fallocate",               // 324
+    "sys_timerfd_settime",         // 325
+    "sys_timerfd_gettime",         // 326
+    "sys_signalfd4",               // 327
+    "sys_eventfd2",                // 328
+    "sys_epoll_create1",           // 329
+    "sys_dup3",                    // 330
+    "sys_pipe2",                   // 331
+    "sys_inotify_init1",           // 332
+    "sys_preadv",                  // 333
+    "sys_pwritev",                 // 334
+    "sys_rt_tgsigqueueinfo",       // 335
+    "sys_perf_event_open",         // 336
+    "sys_recvmmsg",                // 337
+    "sys_fanotify_init",           // 338
+    "sys_fanotify_mark",           // 339
+    "sys_prlimit64",               // 340
+    "sys_name_to_handle_at",       // 341
+    "sys_open_by_handle_at",       // 342
+    "sys_clock_adjtime",           // 343
+    "sys_syncfs",                  // 344
+    "sys_sendmmsg",                // 345
+    "sys_setns",                   // 346
+    "sys_process_vm_readv",        // 347
+    "sys_process_vm_writev",       // 348
+    "sys_kcmp",                    // 349
+    "sys_finit_module",            // 350
+    "sys_sched_setattr",           // 351
+    "sys_sched_getattr",           // 352
+    "sys_renameat2",               // 353
+    "sys_seccomp",                 // 354
+    "sys_getrandom",               // 355
+    "sys_memfd_create",            // 356
+    "sys_bpf",                     // 357
+    "sys_execveat",                // 358
+    "sys_socket",                  // 359
+    "sys_socketpair",              // 360
+    "sys_bind",                    // 361
+    "sys_connect",                 // 362
+    "sys_listen",                  // 363
+    "sys_accept4",                 // 364
+    "sys_getsockopt",              // 365
+    "sys_setsockopt",              // 366
+    "sys_getsockname",             // 367
+    "sys_getpeername",             // 368
+    "sys_sendto",                  // 369
+    "sys_sendmsg",                 // 370
+    "sys_recvfrom",                // 371
+    "sys_recvmsg",                 // 372
+    "sys_shutdown",                // 373
+    "sys_userfaultfd",             // 374
+    "sys_membarrier",              // 375
+    "sys_mlock2",                  // 376
+    "sys_copy_file_range",         // 377
+    "sys_preadv2",                 // 378
+    "sys_pwritev2",                // 379
+    "sys_pkey_mprotect",           // 380
+    "sys_pkey_alloc",              // 381
+    "sys_pkey_free",               // 382
+    "sys_statx",                   // 383
+    "sys_arch_prctl",              // 384
+    "sys_io_pgetevents",           // 385
+    "sys_rseq",                    // 386
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_SYSCALLS_SYSCALLS_X86_H_
diff --git a/src/trace_processor/syscalls_x86_64.h b/src/trace_processor/importers/syscalls/syscalls_x86_64.h
similarity index 98%
rename from src/trace_processor/syscalls_x86_64.h
rename to src/trace_processor/importers/syscalls/syscalls_x86_64.h
index f88573a..01c1b8c 100644
--- a/src/trace_processor/syscalls_x86_64.h
+++ b/src/trace_processor/importers/syscalls/syscalls_x86_64.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_SYSCALLS_X86_64_H_
-#define SRC_TRACE_PROCESSOR_SYSCALLS_X86_64_H_
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_SYSCALLS_SYSCALLS_X86_64_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_SYSCALLS_SYSCALLS_X86_64_H_
 
 namespace perfetto {
 namespace trace_processor {
@@ -575,4 +575,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_SYSCALLS_X86_64_H_
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_SYSCALLS_SYSCALLS_X86_64_H_
diff --git a/src/trace_processor/importers/systrace/systrace_line.h b/src/trace_processor/importers/systrace/systrace_line.h
new file mode 100644
index 0000000..817672b
--- /dev/null
+++ b/src/trace_processor/importers/systrace/systrace_line.h
@@ -0,0 +1,41 @@
+/*
+ * 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_IMPORTERS_SYSTRACE_SYSTRACE_LINE_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_SYSTRACE_SYSTRACE_LINE_H_
+
+#include <inttypes.h>
+#include <string>
+
+namespace perfetto {
+namespace trace_processor {
+
+struct SystraceLine {
+  int64_t ts;
+  uint32_t pid;
+  uint32_t cpu;
+
+  std::string task;
+  std::string pid_str;
+  std::string tgid_str;
+  std::string event_name;
+  std::string args_str;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_SYSTRACE_SYSTRACE_LINE_H_
diff --git a/src/trace_processor/importers/systrace/systrace_line_parser.cc b/src/trace_processor/importers/systrace/systrace_line_parser.cc
new file mode 100644
index 0000000..71f8937
--- /dev/null
+++ b/src/trace_processor/importers/systrace/systrace_line_parser.cc
@@ -0,0 +1,121 @@
+/*
+ * 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/systrace/systrace_line_parser.h"
+
+#include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
+#include "src/trace_processor/importers/systrace/systrace_parser.h"
+#include "src/trace_processor/types/task_state.h"
+
+#include <inttypes.h>
+#include <cctype>
+#include <string>
+#include <unordered_map>
+
+namespace perfetto {
+namespace trace_processor {
+
+SystraceLineParser::SystraceLineParser(TraceProcessorContext* ctx)
+    : context_(ctx),
+      sched_wakeup_name_id_(ctx->storage->InternString("sched_wakeup")),
+      cpuidle_name_id_(ctx->storage->InternString("cpuidle")) {}
+
+util::Status SystraceLineParser::ParseLine(const SystraceLine& line) {
+  context_->process_tracker->GetOrCreateThread(line.pid);
+
+  if (!line.tgid_str.empty() && line.tgid_str != "-----") {
+    base::Optional<uint32_t> tgid = base::StringToUInt32(line.tgid_str);
+    if (tgid) {
+      context_->process_tracker->UpdateThread(line.pid, tgid.value());
+    }
+  }
+
+  std::unordered_map<std::string, std::string> args;
+  for (base::StringSplitter ss(line.args_str, ' '); ss.Next();) {
+    std::string key;
+    std::string value;
+    for (base::StringSplitter inner(ss.cur_token(), '='); inner.Next();) {
+      if (key.empty()) {
+        key = inner.cur_token();
+      } else {
+        value = inner.cur_token();
+      }
+    }
+    args.emplace(std::move(key), std::move(value));
+  }
+  if (line.event_name == "sched_switch") {
+    auto prev_state_str = args["prev_state"];
+    int64_t prev_state =
+        ftrace_utils::TaskState(prev_state_str.c_str()).raw_state();
+
+    auto prev_pid = base::StringToUInt32(args["prev_pid"]);
+    auto prev_comm = base::StringView(args["prev_comm"]);
+    auto prev_prio = base::StringToInt32(args["prev_prio"]);
+    auto next_pid = base::StringToUInt32(args["next_pid"]);
+    auto next_comm = base::StringView(args["next_comm"]);
+    auto next_prio = base::StringToInt32(args["next_prio"]);
+
+    if (!(prev_pid.has_value() && prev_prio.has_value() &&
+          next_pid.has_value() && next_prio.has_value())) {
+      return util::Status("Could not parse sched_switch");
+    }
+
+    SchedEventTracker::GetOrCreate(context_)->PushSchedSwitch(
+        line.cpu, line.ts, prev_pid.value(), prev_comm, prev_prio.value(),
+        prev_state, next_pid.value(), next_comm, next_prio.value());
+  } else if (line.event_name == "tracing_mark_write" ||
+             line.event_name == "0" || line.event_name == "print") {
+    SystraceParser::GetOrCreate(context_)->ParsePrintEvent(
+        line.ts, line.pid, line.args_str.c_str());
+  } else if (line.event_name == "sched_wakeup") {
+    auto comm = args["comm"];
+    base::Optional<uint32_t> wakee_pid = base::StringToUInt32(args["pid"]);
+    if (!wakee_pid.has_value()) {
+      return util::Status("Could not convert wakee_pid");
+    }
+
+    StringId name_id = context_->storage->InternString(base::StringView(comm));
+    auto wakee_utid =
+        context_->process_tracker->UpdateThreadName(wakee_pid.value(), name_id);
+    context_->event_tracker->PushInstant(line.ts, sched_wakeup_name_id_,
+                                         wakee_utid, RefType::kRefUtid);
+  } else if (line.event_name == "cpu_idle") {
+    base::Optional<uint32_t> event_cpu = base::StringToUInt32(args["cpu_id"]);
+    base::Optional<double> new_state = base::StringToDouble(args["state"]);
+    if (!event_cpu.has_value()) {
+      return util::Status("Could not convert event cpu");
+    }
+    if (!event_cpu.has_value()) {
+      return util::Status("Could not convert state");
+    }
+
+    TrackId track = context_->track_tracker->InternCpuCounterTrack(
+        cpuidle_name_id_, event_cpu.value());
+    context_->event_tracker->PushCounter(line.ts, new_state.value(), track);
+  }
+
+  return util::OkStatus();
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/systrace/systrace_line_parser.h b/src/trace_processor/importers/systrace/systrace_line_parser.h
new file mode 100644
index 0000000..00346a3
--- /dev/null
+++ b/src/trace_processor/importers/systrace/systrace_line_parser.h
@@ -0,0 +1,44 @@
+/*
+ * 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_IMPORTERS_SYSTRACE_SYSTRACE_LINE_PARSER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_SYSTRACE_SYSTRACE_LINE_PARSER_H_
+
+#include "perfetto/trace_processor/status.h"
+
+#include "src/trace_processor/importers/systrace/systrace_line.h"
+#include "src/trace_processor/trace_parser.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class SystraceLineParser {
+ public:
+  explicit SystraceLineParser(TraceProcessorContext*);
+
+  util::Status ParseLine(const SystraceLine&);
+
+ private:
+  TraceProcessorContext* const context_;
+  const StringId sched_wakeup_name_id_ = kNullStringId;
+  const StringId cpuidle_name_id_ = kNullStringId;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_SYSTRACE_SYSTRACE_LINE_PARSER_H_
diff --git a/src/trace_processor/importers/systrace/systrace_line_tokenizer.cc b/src/trace_processor/importers/systrace/systrace_line_tokenizer.cc
new file mode 100644
index 0000000..b794aec
--- /dev/null
+++ b/src/trace_processor/importers/systrace/systrace_line_tokenizer.cc
@@ -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.
+ */
+
+#include "src/trace_processor/importers/systrace/systrace_line_tokenizer.h"
+
+#include "perfetto/ext/base/string_utils.h"
+
+// On windows std::isspace if overloaded in <locale>. MSBUILD via bazel
+// attempts to use that version instead of the intended one defined in
+// <cctype>
+#include <cctype>
+
+namespace perfetto {
+namespace trace_processor {
+
+namespace {
+std::string SubstrTrim(const std::string& input) {
+  std::string s = input;
+  s.erase(s.begin(), std::find_if(s.begin(), s.end(),
+                                  [](char ch) { return !std::isspace(ch); }));
+  s.erase(std::find_if(s.rbegin(), s.rend(),
+                       [](char ch) { return !std::isspace(ch); })
+              .base(),
+          s.end());
+  return s;
+}
+}  // namespace
+
+SystraceLineTokenizer::SystraceLineTokenizer()
+    : line_matcher_(std::regex(R"(-(\d+)\s+\(?\s*(\d+|-+)?\)?\s?\[(\d+)\]\s*)"
+                               R"([a-zA-Z0-9.]{0,4}\s+(\d+\.\d+):\s+(\S+):)")) {
+}
+
+// TODO(hjd): This should be more robust to being passed random input.
+// This can happen if we mess up detecting a gzip trace for example.
+util::Status SystraceLineTokenizer::Tokenize(const std::string& buffer,
+                                             SystraceLine* line) {
+  // An example line from buffer looks something like the following:
+  // kworker/u16:1-77    (   77) [004] ....   316.196720: 0:
+  // B|77|__scm_call_armv8_64|0
+  //
+  // However, sometimes the tgid can be missing and buffer looks like this:
+  // <idle>-0     [000] ...2     0.002188: task_newtask: pid=1 ...
+  //
+  // Also the irq fields can be missing (we don't parse these anyway)
+  // <idle>-0     [000]  0.002188: task_newtask: pid=1 ...
+  //
+  // The task name can contain any characters e.g -:[(/ and for this reason
+  // it is much easier to use a regex (even though it is slower than parsing
+  // manually)
+
+  std::smatch matches;
+  bool matched = std::regex_search(buffer, matches, line_matcher_);
+  if (!matched) {
+    return util::ErrStatus("Not a known systrace event format (line: %s)",
+                           buffer.c_str());
+  }
+
+  std::string pid_str = matches[1].str();
+  std::string cpu_str = matches[3].str();
+  std::string ts_str = matches[4].str();
+
+  line->task = SubstrTrim(matches.prefix());
+  line->tgid_str = matches[2].str();
+  line->event_name = matches[5].str();
+  line->args_str = SubstrTrim(matches.suffix());
+
+  base::Optional<uint32_t> maybe_pid = base::StringToUInt32(pid_str);
+  if (!maybe_pid.has_value()) {
+    return util::Status("Could not convert pid " + pid_str);
+  }
+  line->pid = maybe_pid.value();
+
+  base::Optional<uint32_t> maybe_cpu = base::StringToUInt32(cpu_str);
+  if (!maybe_cpu.has_value()) {
+    return util::Status("Could not convert cpu " + cpu_str);
+  }
+  line->cpu = maybe_cpu.value();
+
+  base::Optional<double> maybe_ts = base::StringToDouble(ts_str);
+  if (!maybe_ts.has_value()) {
+    return util::Status("Could not convert ts");
+  }
+  line->ts = static_cast<int64_t>(maybe_ts.value() * 1e9);
+
+  return util::OkStatus();
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/systrace/systrace_line_tokenizer.h b/src/trace_processor/importers/systrace/systrace_line_tokenizer.h
new file mode 100644
index 0000000..10c4a28
--- /dev/null
+++ b/src/trace_processor/importers/systrace/systrace_line_tokenizer.h
@@ -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.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_SYSTRACE_SYSTRACE_LINE_TOKENIZER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_SYSTRACE_SYSTRACE_LINE_TOKENIZER_H_
+
+#include <regex>
+
+#include "perfetto/trace_processor/status.h"
+
+#include "src/trace_processor/importers/systrace/systrace_line.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class SystraceLineTokenizer {
+ public:
+  SystraceLineTokenizer();
+
+  util::Status Tokenize(const std::string& line, SystraceLine*);
+
+ private:
+  const std::regex line_matcher_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_SYSTRACE_SYSTRACE_LINE_TOKENIZER_H_
diff --git a/src/trace_processor/importers/systrace/systrace_parser.cc b/src/trace_processor/importers/systrace/systrace_parser.cc
index 745a447..ddeb4c0 100644
--- a/src/trace_processor/importers/systrace/systrace_parser.cc
+++ b/src/trace_processor/importers/systrace/systrace_parser.cc
@@ -17,10 +17,10 @@
 #include "src/trace_processor/importers/systrace/systrace_parser.h"
 
 #include "perfetto/ext/base/optional.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/track_tracker.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -81,16 +81,27 @@
                                               char trace_type,
                                               bool trace_begin,
                                               base::StringView trace_name,
-                                              uint32_t tgid,
+                                              uint32_t /* tgid */,
                                               int64_t value) {
   systrace_utils::SystraceTracePoint point{};
   point.name = trace_name;
-  point.tgid = tgid;
-  point.value = value;
 
+  // Hardcode the tgid to 0 (i.e. no tgid available) because
+  // sde_tracing_mark_write events can come from kernel threads and because we
+  // group kernel threads into the kthreadd process, we would want |point.tgid
+  // == kKthreaddPid|. However, we don't have acces to the ppid of this process
+  // so we have to not associate to any process and leave the resolution of
+  // process to other events.
+  // TODO(lalitm): remove this hack once we move kernel thread grouping to
+  // the UI.
+  point.tgid = 0;
+
+  point.value = value;
+  // Some versions of this trace point fill trace_type with one of (B/E/C),
+  // others use the trace_begin boolean and only support begin/end events:
   if (trace_type == 0) {
     point.phase = trace_begin ? 'B' : 'E';
-  } else if (trace_type == 'B' && trace_type == 'E' && trace_type == 'C') {
+  } else if (trace_type == 'B' || trace_type == 'E' || trace_type == 'C') {
     point.phase = trace_type;
   } else {
     context_->storage->IncrementStats(stats::systrace_parse_failure);
@@ -107,7 +118,12 @@
   switch (point.phase) {
     case 'B': {
       StringId name_id = context_->storage->InternString(point.name);
-      UniqueTid utid = context_->process_tracker->UpdateThread(pid, point.tgid);
+      UniqueTid utid;
+      if (point.tgid == 0) {
+        utid = context_->process_tracker->GetOrCreateThread(pid);
+      } else {
+        utid = context_->process_tracker->UpdateThread(pid, point.tgid);
+      }
       TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
       context_->slice_tracker->Begin(ts, track_id, kNullStringId /* cat */,
                                      name_id);
diff --git a/src/trace_processor/importers/systrace/systrace_parser.h b/src/trace_processor/importers/systrace/systrace_parser.h
index 97fd337..eaa6f15 100644
--- a/src/trace_processor/importers/systrace/systrace_parser.h
+++ b/src/trace_processor/importers/systrace/systrace_parser.h
@@ -19,12 +19,12 @@
 
 #include <ostream>
 
-#include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/string_view.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/trace_storage.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -48,8 +48,10 @@
 
   static SystraceTracePoint C(uint32_t tgid,
                               base::StringView name,
-                              double value) {
-    return SystraceTracePoint('C', tgid, std::move(name), value);
+                              double value,
+                              base::StringView category_group = "") {
+    return SystraceTracePoint('C', tgid, std::move(name), value,
+                              category_group);
   }
 
   static SystraceTracePoint S(uint32_t tgid,
@@ -64,8 +66,12 @@
     return SystraceTracePoint('F', tgid, std::move(name), value);
   }
 
-  SystraceTracePoint(char p, uint32_t tg, base::StringView n, double v)
-      : phase(p), tgid(tg), name(std::move(n)), value(v) {}
+  SystraceTracePoint(char p,
+                     uint32_t tg,
+                     base::StringView n,
+                     double v,
+                     base::StringView c = "")
+      : phase(p), tgid(tg), name(std::move(n)), value(v), category_group(c) {}
 
   // Phase can be one of B, E, C, S, F.
   char phase = '\0';
@@ -78,6 +84,9 @@
   // For phase = 'C' only.
   double value = 0;
 
+  // For phase = 'C' only (from Chrome).
+  base::StringView category_group;
+
   // Visible for unittesting.
   friend std::ostream& operator<<(std::ostream& os,
                                   const SystraceTracePoint& point) {
@@ -94,6 +103,7 @@
 // 4. C|1636|wq:monitor|0
 // 5. S|1636|frame_capture|123
 // 6. F|1636|frame_capture|456
+// 7. C|3209|TransfersBytesPendingOnDisk-value|0|Blob
 // Visible for unittesting.
 inline SystraceParseResult ParseSystraceTracePoint(base::StringView str,
                                                    SystraceTracePoint* out) {
@@ -101,21 +111,28 @@
   size_t len = str.size();
   *out = {};
 
+  // We don't support empty events.
+  if (len == 0)
+    return SystraceParseResult::kFailure;
+
   constexpr const char* kClockSyncPrefix = "trace_event_clock_sync:";
   if (len >= strlen(kClockSyncPrefix) &&
       strncmp(kClockSyncPrefix, s, strlen(kClockSyncPrefix)) == 0)
     return SystraceParseResult::kUnsupported;
 
-  if (len < 2)
+  char ph = s[0];
+  if (ph != 'B' && ph != 'E' && ph != 'C' && ph != 'S' && ph != 'F')
+    return SystraceParseResult::kFailure;
+
+  out->phase = ph;
+
+  // We only support E events with no arguments.
+  if (len == 1 && ph != 'E')
     return SystraceParseResult::kFailure;
 
   // If str matches '[BEC]\|[0-9]+[\|\n]?' set tgid_length to the length of
   // the number. Otherwise return kFailure.
-  if (s[1] != '|' && s[1] != '\n')
-    return SystraceParseResult::kFailure;
-
-  char ph = s[0];
-  if (ph != 'B' && ph != 'E' && ph != 'C' && ph != 'S' && ph != 'F')
+  if (len >= 2 && s[1] != '|' && s[1] != '\n')
     return SystraceParseResult::kFailure;
 
   size_t tgid_length = 0;
@@ -128,10 +145,11 @@
     tgid_length++;
   }
 
+  // If len == 1, tgid_length will be 0 which will ensure we don't do
+  // an out of bounds read.
   std::string tgid_str(s + 2, tgid_length);
   out->tgid = base::StringToUInt32(tgid_str).value_or(0);
 
-  out->phase = ph;
   switch (ph) {
     case 'B': {
       size_t name_index = 2 + tgid_length + 1;
@@ -160,10 +178,13 @@
       out->name = base::StringView(s + name_index, name_length.value());
 
       size_t value_index = name_index + name_length.value() + 1;
-      size_t value_len = len - value_index;
+      size_t value_pipe = str.find('|', value_index);
+      size_t value_len = value_pipe == base::StringView::npos
+                             ? len - value_index
+                             : value_pipe - value_index;
       if (value_len == 0)
         return SystraceParseResult::kFailure;
-      if (*(s + value_index + value_len - 1) == '\n')
+      if (s[value_index + value_len - 1] == '\n')
         value_len--;
       std::string value_str(s + value_index, value_len);
       base::Optional<double> maybe_value = base::StringToDouble(value_str);
@@ -171,6 +192,16 @@
         return SystraceParseResult::kFailure;
       }
       out->value = maybe_value.value();
+
+      if (value_pipe != base::StringView::npos) {
+        size_t group_len = len - value_pipe - 1;
+        if (group_len == 0)
+          return SystraceParseResult::kFailure;
+        if (s[len - 1] == '\n')
+          group_len--;
+        out->category_group = base::StringView(s + value_pipe + 1, group_len);
+      }
+
       return SystraceParseResult::kSuccess;
     }
     default:
diff --git a/src/trace_processor/importers/systrace/systrace_parser_unittest.cc b/src/trace_processor/importers/systrace/systrace_parser_unittest.cc
index 55d42cc..5c65514 100644
--- a/src/trace_processor/importers/systrace/systrace_parser_unittest.cc
+++ b/src/trace_processor/importers/systrace/systrace_parser_unittest.cc
@@ -37,7 +37,6 @@
   ASSERT_EQ(ParseSystraceTracePoint("|\n", &result), Result::kFailure);
   ASSERT_EQ(ParseSystraceTracePoint("||\n", &result), Result::kFailure);
   ASSERT_EQ(ParseSystraceTracePoint("||\n", &result), Result::kFailure);
-  ASSERT_EQ(ParseSystraceTracePoint("E", &result), Result::kFailure);
   ASSERT_EQ(ParseSystraceTracePoint("B", &result), Result::kFailure);
   ASSERT_EQ(ParseSystraceTracePoint("C", &result), Result::kFailure);
   ASSERT_EQ(ParseSystraceTracePoint("S", &result), Result::kFailure);
@@ -50,6 +49,12 @@
   ASSERT_EQ(ParseSystraceTracePoint("B|42|Bar\n", &result), Result::kSuccess);
   EXPECT_EQ(result, SystraceTracePoint::B(42, "Bar"));
 
+  ASSERT_EQ(ParseSystraceTracePoint("E\n", &result), Result::kSuccess);
+  EXPECT_EQ(result, SystraceTracePoint::E(0));
+
+  ASSERT_EQ(ParseSystraceTracePoint("E", &result), Result::kSuccess);
+  EXPECT_EQ(result, SystraceTracePoint::E(0));
+
   ASSERT_EQ(ParseSystraceTracePoint("E|42\n", &result), Result::kSuccess);
   EXPECT_EQ(result, SystraceTracePoint::E(42));
 
@@ -60,6 +65,11 @@
   ASSERT_EQ(ParseSystraceTracePoint("C|543|foo|8", &result), Result::kSuccess);
   EXPECT_EQ(result, SystraceTracePoint::C(543, "foo", 8));
 
+  ASSERT_EQ(ParseSystraceTracePoint("C|543|foo|8|", &result), Result::kFailure);
+  ASSERT_EQ(ParseSystraceTracePoint("C|543|foo|8|group", &result),
+            Result::kSuccess);
+  EXPECT_EQ(result, SystraceTracePoint::C(543, "foo", 8, "group"));
+
   ASSERT_EQ(ParseSystraceTracePoint("S|", &result), Result::kFailure);
 
   ASSERT_EQ(ParseSystraceTracePoint("S|123|foo|456", &result),
diff --git a/src/trace_processor/importers/systrace/systrace_trace_parser.cc b/src/trace_processor/importers/systrace/systrace_trace_parser.cc
index d23581c..ba0acbd 100644
--- a/src/trace_processor/importers/systrace/systrace_trace_parser.cc
+++ b/src/trace_processor/importers/systrace/systrace_trace_parser.cc
@@ -16,15 +16,11 @@
 
 #include "src/trace_processor/importers/systrace/systrace_trace_parser.h"
 
+#include "perfetto/base/logging.h"
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/ext/base/string_utils.h"
-#include "src/trace_processor/args_tracker.h"
-#include "src/trace_processor/event_tracker.h"
-#include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
-#include "src/trace_processor/importers/systrace/systrace_parser.h"
-#include "src/trace_processor/process_tracker.h"
-#include "src/trace_processor/slice_tracker.h"
-#include "src/trace_processor/track_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/trace_sorter.h"
 
 #include <inttypes.h>
 #include <cctype>
@@ -33,27 +29,39 @@
 
 namespace perfetto {
 namespace trace_processor {
-
 namespace {
-std::string SubstrTrim(const std::string& input) {
-  std::string s = input;
-  s.erase(s.begin(), std::find_if(s.begin(), s.end(),
-                                  [](char ch) { return !std::isspace(ch); }));
-  s.erase(std::find_if(s.rbegin(), s.rend(),
-                       [](char ch) { return !std::isspace(ch); })
-              .base(),
-          s.end());
-  return s;
+
+std::vector<base::StringView> SplitOnSpaces(base::StringView str) {
+  std::vector<base::StringView> result;
+  for (size_t i = 0; i < str.size(); ++i) {
+    // Consume all spaces.
+    for (; i < str.size() && str.data()[i] == ' '; ++i)
+      ;
+    // If we haven't reached the end consume all non-spaces and add result.
+    if (i != str.size()) {
+      size_t start = i;
+      for (; i < str.size() && str.data()[i] != ' '; ++i)
+        ;
+      result.push_back(base::StringView(str.data() + start, i - start));
+    }
+  }
+  return result;
 }
+
+bool IsProcessDumpShortHeader(const std::vector<base::StringView>& tokens) {
+  return tokens.size() == 4 && tokens[0] == "USER" && tokens[1] == "PID" &&
+         tokens[2] == "TID" && tokens[3] == "CMD";
+}
+
+bool IsProcessDumpLongHeader(const std::vector<base::StringView>& tokens) {
+  return tokens.size() > 4 && tokens[0] == "USER" && tokens[1] == "PID" &&
+         tokens[2] == "PPID" && tokens[3] == "VSZ";
+}
+
 }  // namespace
 
 SystraceTraceParser::SystraceTraceParser(TraceProcessorContext* ctx)
-    : context_(ctx),
-      sched_wakeup_name_id_(ctx->storage->InternString("sched_wakeup")),
-      cpu_idle_name_id_(ctx->storage->InternString("cpuidle")),
-      line_matcher_(std::regex(R"(-(\d+)\s+\(?\s*(\d+|-+)?\)?\s?\[(\d+)\]\s*)"
-                               R"([a-zA-Z0-9.]{0,4}\s+(\d+\.\d+):\s+(\S+):)")) {
-}
+    : line_parser_(ctx), ctx_(ctx) {}
 SystraceTraceParser::~SystraceTraceParser() = default;
 
 util::Status SystraceTraceParser::Parse(std::unique_ptr<uint8_t[]> owned_buf,
@@ -87,6 +95,8 @@
     } else if (state_ == ParseState::kTraceDataSection) {
       if (base::StartsWith(buffer, "#")) {
         state_ = ParseState::kSystrace;
+      } else if (base::StartsWith(buffer, "PROCESS DUMP")) {
+        state_ = ParseState::kProcessDumpLong;
       } else if (base::Contains(buffer, R"(</script>)")) {
         state_ = ParseState::kHtmlBeforeSystrace;
       }
@@ -94,8 +104,66 @@
       if (base::Contains(buffer, R"(</script>)")) {
         state_ = ParseState::kEndOfSystrace;
         break;
-      } else if (!base::StartsWith(buffer, "#")) {
-        ParseSingleSystraceEvent(buffer);
+      } else if (!base::StartsWith(buffer, "#") && !buffer.empty()) {
+        SystraceLine line;
+        util::Status status = line_tokenizer_.Tokenize(buffer, &line);
+        if (!status.ok())
+          return status;
+        line_parser_.ParseLine(std::move(line));
+      }
+    } else if (state_ == ParseState::kProcessDumpLong ||
+               state_ == ParseState::kProcessDumpShort) {
+      if (base::Contains(buffer, R"(</script>)")) {
+        state_ = ParseState::kHtmlBeforeSystrace;
+      } else {
+        std::vector<base::StringView> tokens =
+            SplitOnSpaces(base::StringView(buffer));
+        if (IsProcessDumpShortHeader(tokens)) {
+          state_ = ParseState::kProcessDumpShort;
+        } else if (IsProcessDumpLongHeader(tokens)) {
+          state_ = ParseState::kProcessDumpLong;
+        } else if (state_ == ParseState::kProcessDumpLong &&
+                   tokens.size() >= 10) {
+          // Format is:
+          // user pid ppid vsz rss wchan pc s name my cmd line
+          const base::Optional<uint32_t> pid =
+              base::StringToUInt32(tokens[1].ToStdString());
+          const base::Optional<uint32_t> ppid =
+              base::StringToUInt32(tokens[2].ToStdString());
+          base::StringView name = tokens[8];
+          // Command line may contain spaces, merge all remaining tokens:
+          const char* cmd_start = tokens[9].data();
+          base::StringView cmd(
+              cmd_start,
+              static_cast<size_t>((buffer.data() + buffer.size()) - cmd_start));
+          if (!pid || !ppid) {
+            PERFETTO_ELOG("Could not parse line '%s'", buffer.c_str());
+            return util::ErrStatus("Could not parse PROCESS DUMP line");
+          }
+          ctx_->process_tracker->SetProcessMetadata(pid.value(), ppid, name);
+        } else if (state_ == ParseState::kProcessDumpShort &&
+                   tokens.size() >= 4) {
+          // Format is:
+          // username pid tid my cmd line
+          const base::Optional<uint32_t> tgid =
+              base::StringToUInt32(tokens[1].ToStdString());
+          const base::Optional<uint32_t> tid =
+              base::StringToUInt32(tokens[2].ToStdString());
+          // Command line may contain spaces, merge all remaining tokens:
+          const char* cmd_start = tokens[3].data();
+          base::StringView cmd(
+              cmd_start,
+              static_cast<size_t>((buffer.data() + buffer.size()) - cmd_start));
+          StringId cmd_id =
+              ctx_->storage->mutable_string_pool()->InternString(cmd);
+          if (!tid || !tgid) {
+            PERFETTO_ELOG("Could not parse line '%s'", buffer.c_str());
+            return util::ErrStatus("Could not parse PROCESS DUMP line");
+          }
+          UniqueTid utid =
+              ctx_->process_tracker->UpdateThread(tid.value(), tgid.value());
+          ctx_->process_tracker->SetThreadNameIfUnset(utid, cmd_id);
+        }
       }
     }
     start_it = line_it + 1;
@@ -108,130 +176,7 @@
   return util::OkStatus();
 }
 
-// TODO(hjd): This should be more robust to being passed random input.
-// This can happen if we mess up detecting a gzip trace for example.
-util::Status SystraceTraceParser::ParseSingleSystraceEvent(
-    const std::string& buffer) {
-  // An example line from buffer looks something like the following:
-  // kworker/u16:1-77    (   77) [004] ....   316.196720: 0:
-  // B|77|__scm_call_armv8_64|0
-  //
-  // However, sometimes the tgid can be missing and buffer looks like this:
-  // <idle>-0     [000] ...2     0.002188: task_newtask: pid=1 ...
-  //
-  // Also the irq fields can be missing (we don't parse these anyway)
-  // <idle>-0     [000]  0.002188: task_newtask: pid=1 ...
-  //
-  // The task name can contain any characters e.g -:[(/ and for this reason
-  // it is much easier to use a regex (even though it is slower than parsing
-  // manually)
-
-  std::smatch matches;
-  bool matched = std::regex_search(buffer, matches, line_matcher_);
-  if (!matched) {
-    return util::Status("Not a known systrace event format");
-  }
-
-  std::string task = SubstrTrim(matches.prefix());
-  std::string pid_str = matches[1].str();
-  std::string tgid_str = matches[2].str();
-  std::string cpu_str = matches[3].str();
-  std::string ts_str = matches[4].str();
-  std::string event_name = matches[5].str();
-  std::string args_str = SubstrTrim(matches.suffix());
-
-  base::Optional<uint32_t> maybe_pid = base::StringToUInt32(pid_str);
-  if (!maybe_pid.has_value()) {
-    return util::Status("Could not convert pid " + pid_str);
-  }
-  uint32_t pid = maybe_pid.value();
-  context_->process_tracker->GetOrCreateThread(pid);
-
-  if (tgid_str != "" && tgid_str != "-----") {
-    base::Optional<uint32_t> tgid = base::StringToUInt32(tgid_str);
-    if (tgid) {
-      context_->process_tracker->UpdateThread(pid, tgid.value());
-    }
-  }
-
-  base::Optional<uint32_t> maybe_cpu = base::StringToUInt32(cpu_str);
-  if (!maybe_cpu.has_value()) {
-    return util::Status("Could not convert cpu " + cpu_str);
-  }
-  uint32_t cpu = maybe_cpu.value();
-
-  base::Optional<double> maybe_ts = base::StringToDouble(ts_str);
-  if (!maybe_ts.has_value()) {
-    return util::Status("Could not convert ts");
-  }
-  int64_t ts = static_cast<int64_t>(maybe_ts.value() * 1e9);
-
-  std::unordered_map<std::string, std::string> args;
-  for (base::StringSplitter ss(args_str.c_str(), ' '); ss.Next();) {
-    std::string key;
-    std::string value;
-    for (base::StringSplitter inner(ss.cur_token(), '='); inner.Next();) {
-      if (key.empty()) {
-        key = inner.cur_token();
-      } else {
-        value = inner.cur_token();
-      }
-    }
-    args.emplace(std::move(key), std::move(value));
-  }
-  if (event_name == "sched_switch") {
-    auto prev_state_str = args["prev_state"];
-    int64_t prev_state =
-        ftrace_utils::TaskState(prev_state_str.c_str()).raw_state();
-
-    auto prev_pid = base::StringToUInt32(args["prev_pid"]);
-    auto prev_comm = base::StringView(args["prev_comm"]);
-    auto prev_prio = base::StringToInt32(args["prev_prio"]);
-    auto next_pid = base::StringToUInt32(args["next_pid"]);
-    auto next_comm = base::StringView(args["next_comm"]);
-    auto next_prio = base::StringToInt32(args["next_prio"]);
-
-    if (!(prev_pid.has_value() && prev_prio.has_value() &&
-          next_pid.has_value() && next_prio.has_value())) {
-      return util::Status("Could not parse sched_switch");
-    }
-
-    SchedEventTracker::GetOrCreate(context_)->PushSchedSwitch(
-        cpu, ts, prev_pid.value(), prev_comm, prev_prio.value(), prev_state,
-        next_pid.value(), next_comm, next_prio.value());
-  } else if (event_name == "tracing_mark_write" || event_name == "0" ||
-             event_name == "print") {
-    SystraceParser::GetOrCreate(context_)->ParsePrintEvent(ts, pid,
-                                                           args_str.c_str());
-  } else if (event_name == "sched_wakeup") {
-    auto comm = args["comm"];
-    base::Optional<uint32_t> wakee_pid = base::StringToUInt32(args["pid"]);
-    if (!wakee_pid.has_value()) {
-      return util::Status("Could not convert wakee_pid");
-    }
-
-    StringId name_id = context_->storage->InternString(base::StringView(comm));
-    auto wakee_utid =
-        context_->process_tracker->UpdateThreadName(wakee_pid.value(), name_id);
-    context_->event_tracker->PushInstant(ts, sched_wakeup_name_id_, wakee_utid,
-                                         RefType::kRefUtid);
-  } else if (event_name == "cpu_idle") {
-    base::Optional<uint32_t> event_cpu = base::StringToUInt32(args["cpu_id"]);
-    base::Optional<double> new_state = base::StringToDouble(args["state"]);
-    if (!event_cpu.has_value()) {
-      return util::Status("Could not convert event cpu");
-    }
-    if (!event_cpu.has_value()) {
-      return util::Status("Could not convert state");
-    }
-
-    TrackId track = context_->track_tracker->InternCpuCounterTrack(
-        cpu_idle_name_id_, event_cpu.value());
-    context_->event_tracker->PushCounter(ts, new_state.value(), track);
-  }
-
-  return util::OkStatus();
-}
+void SystraceTraceParser::NotifyEndOfFile() {}
 
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/systrace/systrace_trace_parser.h b/src/trace_processor/importers/systrace/systrace_trace_parser.h
index 26553e9..09c82bb 100644
--- a/src/trace_processor/importers/systrace/systrace_trace_parser.h
+++ b/src/trace_processor/importers/systrace/systrace_trace_parser.h
@@ -21,8 +21,10 @@
 #include <regex>
 
 #include "src/trace_processor/chunked_trace_reader.h"
-#include "src/trace_processor/trace_processor_context.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/importers/systrace/systrace_line_parser.h"
+#include "src/trace_processor/importers/systrace/systrace_line_tokenizer.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -34,6 +36,7 @@
 
   // ChunkedTraceReader implementation.
   util::Status Parse(std::unique_ptr<uint8_t[]>, size_t size) override;
+  void NotifyEndOfFile() override;
 
  private:
   enum ParseState {
@@ -41,21 +44,20 @@
     kHtmlBeforeSystrace,
     kTraceDataSection,
     kSystrace,
+    kProcessDumpLong,
+    kProcessDumpShort,
     kEndOfSystrace,
   };
 
-  util::Status ParseSingleSystraceEvent(const std::string& buffer);
-
-  TraceProcessorContext* const context_;
-  const StringId sched_wakeup_name_id_ = kNullStringId;
-  const StringId cpu_idle_name_id_ = kNullStringId;
-  const std::regex line_matcher_;
-
   ParseState state_ = ParseState::kBeforeParse;
 
   // Used to glue together trace packets that span across two (or more)
   // Parse() boundaries.
   std::deque<uint8_t> partial_buf_;
+
+  SystraceLineTokenizer line_tokenizer_;
+  SystraceLineParser line_parser_;
+  TraceProcessorContext* ctx_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/metrics/BUILD.gn b/src/trace_processor/metrics/BUILD.gn
index 89febb8..b6595b9 100644
--- a/src/trace_processor/metrics/BUILD.gn
+++ b/src/trace_processor/metrics/BUILD.gn
@@ -29,10 +29,15 @@
   "android/android_startup_launches.sql",
   "android/android_task_state.sql",
   "android/android_startup.sql",
-  "android/android_startup_cpu.sql",
   "android/android_package_list.sql",
+  "android/android_task_names.sql",
+  "android/android_thread_time_in_state.sql",
+  "android/cpu_info.sql",
+  "android/display_metrics.sql",
   "android/heap_profile_callsites.sql",
+  "android/hsc_startups.sql",
   "android/android_hwui_metric.sql",
+  "android/java_heap_histogram.sql",
   "android/java_heap_stats.sql",
   "android/process_unagg_mem_view.sql",
   "android/process_mem.sql",
@@ -57,9 +62,7 @@
            rebase_path(generated_header, root_build_dir),
          ]
   inputs = sql_files
-  outputs = [
-    generated_header,
-  ]
+  outputs = [ generated_header ]
   public_configs = [ ":gen_config" ]
 }
 
@@ -85,17 +88,16 @@
     ]
     public_deps = [
       ":gen_merged_sql_metrics",
-      "../../trace_processor:descriptors",
+      "../util:descriptors",
     ]
   }
 
   perfetto_unittest_source_set("unittests") {
     testonly = true
-    sources = [
-      "metrics_unittest.cc",
-    ]
+    sources = [ "metrics_unittest.cc" ]
     deps = [
       ":lib",
+      "..:lib",
       "../../../gn:default_deps",
       "../../../gn:gtest_and_gmock",
       "../../../gn:sqlite",
diff --git a/src/trace_processor/metrics/android/android_batt.sql b/src/trace_processor/metrics/android/android_batt.sql
index 851ee05..94c92a6 100644
--- a/src/trace_processor/metrics/android/android_batt.sql
+++ b/src/trace_processor/metrics/android/android_batt.sql
@@ -52,6 +52,10 @@
 ) USING(ts)
 ORDER BY ts;
 
+SELECT RUN_METRIC('android/upid_span_view.sql',
+  'table_name', 'screen_state',
+  'counter_name', 'ScreenState');
+
 CREATE VIEW android_batt_output AS
 SELECT AndroidBatteryMetric(
   'battery_counters', (
@@ -65,5 +69,16 @@
       )
     )
     FROM battery_view
+  ),
+  'battery_aggregates', (
+    SELECT AndroidBatteryMetric_BatteryAggregates(
+      'total_screen_off_ns',
+      SUM(CASE WHEN screen_state_val = 1.0 THEN dur ELSE 0 END),
+      'total_screen_on_ns',
+      SUM(CASE WHEN screen_state_val = 2.0 THEN dur ELSE 0 END),
+      'total_screen_doze_ns',
+      SUM(CASE WHEN screen_state_val = 3.0 THEN dur ELSE 0 END)
+    )
+    FROM screen_state_span
   )
 );
diff --git a/src/trace_processor/metrics/android/android_cpu.sql b/src/trace_processor/metrics/android/android_cpu.sql
index 8e8a031..44fe394 100644
--- a/src/trace_processor/metrics/android/android_cpu.sql
+++ b/src/trace_processor/metrics/android/android_cpu.sql
@@ -16,35 +16,7 @@
 
 -- Create all the views used to generate the Android Cpu metrics proto.
 SELECT RUN_METRIC('android/android_cpu_agg.sql');
-
-CREATE VIEW core_layout_mapping AS
-SELECT
-  CASE
-    WHEN (
-      str_value LIKE '%flame%' OR
-      str_value LIKE '%coral%'
-    ) THEN 'big_little_bigger'
-    WHEN (
-      str_value LIKE '%taimen%' OR
-      str_value LIKE '%walleye%' OR
-      str_value LIKE '%bonito%' OR
-      str_value LIKE '%sargo%' OR
-      str_value LIKE '%blueline%' OR
-      str_value LIKE '%crosshatch%'
-    ) THEN 'big_little'
-    ELSE 'unknown'
-  END AS layout
-FROM metadata
-WHERE name = 'android_build_fingerprint';
-
-CREATE TABLE core_layout_type AS
-SELECT *
-FROM (
-  SELECT layout from core_layout_mapping
-  UNION
-  SELECT 'unknown'
-)
-LIMIT 1;
+SELECT RUN_METRIC('android/cpu_info.sql');
 
 CREATE TABLE raw_metrics_per_core AS
 SELECT
@@ -62,12 +34,21 @@
       END
     FROM core_layout_type
   ) AS core_type,
-  CAST(SUM(dur * freq) AS INT) AS cycles,
-  CAST(SUM(dur * freq / 1000000) AS INT) AS mcycles,
-  CAST(SUM(dur) AS INT) AS runtime_ns,
-  CAST(MIN(freq) AS INT) AS min_freq_khz,
-  CAST(MAX(freq) AS INT) AS max_freq_khz,
-  CAST((SUM(dur * freq) / SUM(dur)) AS INT) AS avg_freq_khz
+  -- We divide by 1e3 here as dur is in ns and freq_khz in khz. In total
+  -- this means we need to divide the duration by 1e9 and multiply the
+  -- frequency by 1e3 then multiply again by 1e3 to get millicycles
+  -- i.e. divide by 1e3 in total.
+  -- We use millicycles as we want to preserve this level of precision
+  -- for future calculations.
+  SUM(dur * freq_khz / 1000) AS millicycles,
+  CAST(SUM(dur * freq_khz / 1000000 / 1000000) AS INT) AS mcycles,
+  SUM(dur) AS runtime_ns,
+  MIN(freq_khz) AS min_freq_khz,
+  MAX(freq_khz) AS max_freq_khz,
+  -- We choose to work in micros space in both the numerator and
+  -- denominator as this gives us good enough precision without risking
+  -- overflows.
+  CAST(SUM(dur * freq_khz / 1000) / SUM(dur / 1000) AS INT) AS avg_freq_khz
 FROM cpu_freq_sched_per_thread
 GROUP BY utid, cpu;
 
@@ -80,7 +61,11 @@
     'runtime_ns', SUM(runtime_ns),
     'min_freq_khz', MIN(min_freq_khz),
     'max_freq_khz', MAX(max_freq_khz),
-    'avg_freq_khz', (SUM(cycles) / SUM(runtime_ns))
+    -- In total here, we need to divide the denominator by 1e9 (to convert
+    -- ns to s) and divide the numerator by 1e6 (to convert millicycles to
+    -- kcycles). In total, this means we need to multiply the expression as
+    -- a whole by 1e3.
+    'avg_freq_khz', CAST((SUM(millicycles) / SUM(runtime_ns)) * 1000 AS INT)
   ) AS proto
 FROM raw_metrics_per_core
 GROUP BY utid, core_type;
@@ -124,7 +109,9 @@
     'runtime_ns', SUM(runtime_ns),
     'min_freq_khz', MIN(min_freq_khz),
     'max_freq_khz', MAX(max_freq_khz),
-    'avg_freq_khz', (SUM(cycles) / SUM(runtime_ns))
+    -- See above for a breakdown of the maths used to compute the
+    -- multiplicative factor.
+    'avg_freq_khz', CAST((SUM(millicycles) / SUM(runtime_ns)) * 1000 AS INT)
   ) AS proto
 FROM raw_metrics_per_core
 GROUP BY utid;
@@ -147,6 +134,68 @@
 LEFT JOIN metrics_proto_per_thread USING(utid)
 GROUP BY upid;
 
+CREATE VIEW core_metrics_per_process AS
+SELECT
+  upid,
+  cpu,
+  AndroidCpuMetric_Metrics(
+    'mcycles', SUM(mcycles),
+    'runtime_ns', SUM(runtime_ns),
+    'min_freq_khz', MIN(min_freq_khz),
+    'max_freq_khz', MAX(max_freq_khz),
+    -- In total here, we need to divide the denominator by 1e9 (to convert
+    -- ns to s) and divide the numerator by 1e6 (to convert millicycles to
+    -- kcycles). In total, this means we need to multiply the expression as
+    -- a whole by 1e3.
+    'avg_freq_khz', CAST((SUM(millicycles) / SUM(runtime_ns)) * 1000 AS INT)
+  ) AS proto
+FROM raw_metrics_per_core
+JOIN thread USING (utid)
+GROUP BY upid, cpu;
+
+CREATE VIEW core_proto_per_process AS
+SELECT
+  upid,
+  RepeatedField(
+    AndroidCpuMetric_CoreData(
+      'id', cpu,
+      'metrics', core_metrics_per_process.proto
+    )
+  ) as proto
+FROM core_metrics_per_process
+GROUP BY upid;
+
+CREATE VIEW core_type_metrics_per_process AS
+SELECT
+  upid,
+  core_type,
+  AndroidCpuMetric_Metrics(
+    'mcycles', SUM(mcycles),
+    'runtime_ns', SUM(runtime_ns),
+    'min_freq_khz', MIN(min_freq_khz),
+    'max_freq_khz', MAX(max_freq_khz),
+    -- In total here, we need to divide the denominator by 1e9 (to convert
+    -- ns to s) and divide the numerator by 1e6 (to convert millicycles to
+    -- kcycles). In total, this means we need to multiply the expression as
+    -- a whole by 1e3.
+    'avg_freq_khz', CAST((SUM(millicycles) / SUM(runtime_ns)) * 1000 AS INT)
+  ) AS proto
+FROM raw_metrics_per_core
+JOIN thread USING (utid)
+GROUP BY upid, core_type;
+
+CREATE VIEW core_type_proto_per_process AS
+SELECT
+  upid,
+  RepeatedField(
+    AndroidCpuMetric_CoreTypeData(
+      'type', core_type,
+      'metrics', core_type_metrics_per_process.proto
+    )
+  ) as proto
+FROM core_type_metrics_per_process
+GROUP BY upid;
+
 CREATE VIEW metrics_proto_per_process AS
 SELECT
   upid,
@@ -155,7 +204,9 @@
     'runtime_ns', SUM(runtime_ns),
     'min_freq_khz', MIN(min_freq_khz),
     'max_freq_khz', MAX(max_freq_khz),
-    'avg_freq_khz', (SUM(cycles) / SUM(runtime_ns))
+    -- See above for a breakdown of the maths used to compute the
+    -- multiplicative factor.
+    'avg_freq_khz', CAST((SUM(millicycles) / SUM(runtime_ns)) * 1000 AS INT)
   ) AS proto
 FROM raw_metrics_per_core
 JOIN thread USING (utid)
@@ -168,11 +219,15 @@
       AndroidCpuMetric_Process(
         'name', process.name,
         'metrics', metrics_proto_per_process.proto,
-        'threads', thread_proto_per_process.proto
+        'threads', thread_proto_per_process.proto,
+        'core', core_proto_per_process.proto,
+        'core_type', core_type_proto_per_process.proto
       )
     )
     FROM process
     JOIN metrics_proto_per_process USING(upid)
     JOIN thread_proto_per_process USING (upid)
+    JOIN core_proto_per_process USING (upid)
+    JOIN core_type_proto_per_process USING (upid)
   )
 );
diff --git a/src/trace_processor/metrics/android/android_cpu_agg.sql b/src/trace_processor/metrics/android/android_cpu_agg.sql
index 68c887a..521f580 100644
--- a/src/trace_processor/metrics/android/android_cpu_agg.sql
+++ b/src/trace_processor/metrics/android/android_cpu_agg.sql
@@ -22,7 +22,7 @@
   ts,
   LEAD(ts, 1, (SELECT end_ts from trace_bounds))
     OVER (PARTITION by cpu ORDER BY ts) - ts AS dur,
-  value as freq
+  CAST(value AS INT) as freq_khz
 FROM counter
 JOIN cpu_counter_track on counter.track_id = cpu_counter_track.id
 WHERE name = 'cpufreq';
diff --git a/src/trace_processor/metrics/android/android_hwui_metric.sql b/src/trace_processor/metrics/android/android_hwui_metric.sql
index f7ba671..4943f2e 100644
--- a/src/trace_processor/metrics/android/android_hwui_metric.sql
+++ b/src/trace_processor/metrics/android/android_hwui_metric.sql
@@ -143,7 +143,7 @@
   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
+WHERE name='HWUI Misc Memory' AND counter.value >= 0
 GROUP BY process_counter_track.upid;
 
 CREATE VIEW hwui_texture_mem AS
diff --git a/src/trace_processor/metrics/android/android_ion.sql b/src/trace_processor/metrics/android/android_ion.sql
index 9b30a3c..4372f50 100644
--- a/src/trace_processor/metrics/android/android_ion.sql
+++ b/src/trace_processor/metrics/android/android_ion.sql
@@ -20,12 +20,13 @@
   LEAD(ts, 1, (SELECT end_ts FROM trace_bounds))
     OVER(PARTITION BY track_id ORDER BY ts) - ts AS dur,
   SUBSTR(name, 9) AS heap_name,
+  track_id,
   value
 FROM counter JOIN counter_track
   ON counter.track_id = counter_track.id
 WHERE name LIKE 'mem.ion.%';
 
-CREATE VIEW IF NOT EXISTS ion_buffers AS
+CREATE VIEW IF NOT EXISTS ion_heap_stats AS
 SELECT
   heap_name,
   SUM(value * dur) / SUM(dur) AS avg_size,
@@ -34,6 +35,34 @@
 FROM ion_timeline
 GROUP BY 1;
 
+CREATE VIEW IF NOT EXISTS ion_raw_allocs AS
+SELECT
+  SUBSTR(name, 16) AS heap_name,
+  ts,
+  value AS instant_value,
+  SUM(value) OVER win AS value
+FROM counter c JOIN thread_counter_track t ON c.track_id = t.id
+WHERE name LIKE 'mem.ion_change.%' AND value > 0
+WINDOW win AS (
+  PARTITION BY SUBSTR(name, 16) ORDER BY ts
+  ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
+);
+
+CREATE VIEW IF NOT EXISTS ion_alloc_stats AS
+SELECT
+  heap_name,
+  SUM(instant_value) AS total_alloc_size_bytes
+FROM ion_raw_allocs
+GROUP BY 1;
+
+CREATE VIEW IF NOT EXISTS android_ion_annotations AS
+SELECT
+  'counter' AS track_type,
+  printf('ION allocations (heap: %s)', heap_name) AS track_name,
+  ts,
+  value
+FROM ion_raw_allocs;
+
 CREATE VIEW IF NOT EXISTS android_ion_output AS
 SELECT AndroidIonMetric(
   'buffer', RepeatedField(
@@ -41,7 +70,8 @@
       'name', heap_name,
       'avg_size_bytes', avg_size,
       'min_size_bytes', min_size,
-      'max_size_bytes', max_size
+      'max_size_bytes', max_size,
+      'total_alloc_size_bytes', total_alloc_size_bytes
     )
   ))
-FROM ion_buffers;
+FROM ion_heap_stats JOIN ion_alloc_stats USING (heap_name);
diff --git a/src/trace_processor/metrics/android/android_lmk.sql b/src/trace_processor/metrics/android/android_lmk.sql
index 8b96ab7..8fc96e5 100644
--- a/src/trace_processor/metrics/android/android_lmk.sql
+++ b/src/trace_processor/metrics/android/android_lmk.sql
@@ -48,5 +48,8 @@
       ))
     FROM lmk_counts
     WHERE score IS NOT NULL
+  ),
+  'oom_victim_count', (
+    SELECT COUNT(1) FROM instants WHERE name = 'mem.oom_kill'
   )
 );
diff --git a/src/trace_processor/metrics/android/android_package_list.sql b/src/trace_processor/metrics/android/android_package_list.sql
index 7ca9c15..5e05281 100644
--- a/src/trace_processor/metrics/android/android_package_list.sql
+++ b/src/trace_processor/metrics/android/android_package_list.sql
@@ -14,43 +14,6 @@
 -- limitations under the License.
 --
 
--- Get distinct packages list
-DROP VIEW IF EXISTS package_arg_ids;
-
-CREATE VIEW package_arg_ids AS
-SELECT int_value AS arg_set_id
-FROM metadata WHERE name = 'android_packages_list';
-
--- Generate a table mapping package names to their attributes
-DROP VIEW IF EXISTS package_args;
-
-CREATE VIEW package_args AS
-SELECT arg_set_id, key, string_value, int_value
-FROM package_arg_ids JOIN args USING(arg_set_id);
-
-DROP TABLE IF EXISTS package_list;
-
-CREATE TABLE package_list(
-  package_name TEXT PRIMARY KEY,
-  uid INT,
-  version_code INT,
-  debuggable INT
-);
-
-INSERT OR REPLACE INTO package_list
-SELECT names.name, uids.uid, versions.version, debuggable.is_debug
-FROM
-  (SELECT arg_set_id, string_value name FROM package_args WHERE key = 'name')
-    AS names
-  JOIN (SELECT arg_set_id, int_value uid FROM package_args WHERE key = 'uid')
-    AS uids USING (arg_set_id)
-  JOIN (SELECT arg_set_id, int_value version FROM package_args WHERE key = 'version_code')
-    AS versions USING (arg_set_id)
-  JOIN (SELECT arg_set_id, int_value is_debug FROM package_args WHERE key = 'debuggable')
-    AS debuggable USING (arg_set_id);
-
-DROP VIEW IF EXISTS android_package_list_output;
-
 CREATE VIEW android_package_list_output AS
 SELECT AndroidPackageList(
   'packages', (
diff --git a/src/trace_processor/metrics/android/android_startup.sql b/src/trace_processor/metrics/android/android_startup.sql
index 142d142..944902d 100644
--- a/src/trace_processor/metrics/android/android_startup.sql
+++ b/src/trace_processor/metrics/android/android_startup.sql
@@ -17,7 +17,8 @@
 -- Create the base tables and views containing the launch spans.
 SELECT RUN_METRIC('android/android_startup_launches.sql');
 SELECT RUN_METRIC('android/android_task_state.sql');
-SELECT RUN_METRIC('android/android_startup_cpu.sql');
+SELECT RUN_METRIC('android/process_metadata.sql');
+SELECT RUN_METRIC('android/hsc_startups.sql');
 
 -- Slices for forked processes. Never present in hot starts.
 -- Prefer this over process start_ts, since the process might have
@@ -65,7 +66,10 @@
 SELECT
   launches.id AS launch_id,
   slice.name AS name,
-  AndroidStartupMetric_Slice('dur_ns', SUM(slice.dur)) AS slice_proto
+  AndroidStartupMetric_Slice(
+    'dur_ns', SUM(slice.dur),
+    'dur_ms', SUM(slice.dur) / 1e6
+  ) AS slice_proto
 FROM launches
 JOIN launch_processes ON (launches.id = launch_processes.launch_id)
 JOIN thread ON (launch_processes.upid = thread.upid)
@@ -74,6 +78,7 @@
   slice.track_id = thread_track.id
   AND slice.ts BETWEEN launches.ts AND launches.ts + launches.dur)
 WHERE slice.name IN (
+  'PostFork',
   'ActivityThreadMain',
   'bindApplication',
   'activityStart',
@@ -82,6 +87,20 @@
   'inflate')
 GROUP BY 1, 2;
 
+CREATE VIEW to_event_protos AS
+SELECT
+  slice.name as slice_name,
+  launch_id,
+  AndroidStartupMetric_Slice(
+    'dur_ns', slice.ts - l.ts,
+    'dur_ms', (slice.ts - l.ts) / 1e6
+  ) as slice_proto
+FROM launch_main_threads l
+JOIN thread_track USING (utid)
+JOIN slice ON (
+  slice.track_id = thread_track.id
+  AND slice.ts BETWEEN l.ts AND l.ts + l.dur);
+
 CREATE VIEW startup_view AS
 SELECT
   AndroidStartupMetric_Startup(
@@ -95,12 +114,21 @@
         LIMIT 1
       )
     ),
+    'process', (
+      SELECT metadata FROM process_metadata
+      WHERE upid IN (
+        SELECT upid FROM launch_processes
+        WHERE launch_id = launches.id
+        LIMIT 1
+      )
+    ),
     'zygote_new_process', EXISTS(SELECT TRUE FROM zygote_forks_by_id WHERE id = launches.id),
     'activity_hosting_process_count', (
       SELECT COUNT(1) FROM launch_processes WHERE launch_id = launches.id
     ),
     'to_first_frame', AndroidStartupMetric_ToFirstFrame(
       'dur_ns', launches.dur,
+      'dur_ms', launches.dur / 1e6,
       'main_thread_by_task_state', AndroidStartupMetric_TaskStateBreakdown(
         'running_dur_ns', IFNULL(
             (
@@ -123,17 +151,37 @@
             WHERE launch_id = launches.id AND state = 'interruptible'
             ), 0)
       ),
+      'to_post_fork', (
+        SELECT slice_proto
+        FROM to_event_protos
+        WHERE launch_id = launches.id AND slice_name = 'PostFork'
+      ),
+      'to_activity_thread_main', (
+        SELECT slice_proto
+        FROM to_event_protos
+        WHERE launch_id = launches.id AND slice_name = 'ActivityThreadMain'
+      ),
+      'to_bind_application', (
+        SELECT slice_proto
+        FROM to_event_protos
+        WHERE launch_id = launches.id AND slice_name = 'bindApplication'
+      ),
       'other_processes_spawned_count', (
         SELECT COUNT(1) FROM process
         WHERE (process.name IS NULL OR process.name != launches.package)
         AND process.start_ts BETWEEN launches.ts AND launches.ts + launches.dur
       ),
-      'time_activity_manager', AndroidStartupMetric_Slice(
-        'dur_ns', (
-          SELECT launching_events.ts - launches.ts FROM launching_events
-          WHERE launching_events.type = 'S'
-          AND launching_events.ts BETWEEN launches.ts AND launches.ts + launches.dur
+      'time_activity_manager', (
+        SELECT AndroidStartupMetric_Slice(
+          'dur_ns', launching_events.ts - launches.ts,
+          'dur_ms', (launching_events.ts - launches.ts) / 1e6
         )
+        FROM launching_events
+        WHERE launching_events.ts BETWEEN launches.ts AND launches.ts + launches.dur
+      ),
+      'time_post_fork', (
+        SELECT slice_proto FROM main_process_slice
+        WHERE launch_id = launches.id AND name = 'PostFork'
       ),
       'time_activity_thread_main', (
         SELECT slice_proto FROM main_process_slice
@@ -156,20 +204,43 @@
         WHERE launch_id = launches.id AND name = 'Choreographer#doFrame'
       ),
       'time_before_start_process', (
-        SELECT AndroidStartupMetric_Slice('dur_ns', ts - launches.ts)
+        SELECT AndroidStartupMetric_Slice(
+          'dur_ns', ts - launches.ts,
+          'dur_ms', (ts - launches.ts) / 1e6
+        )
         FROM zygote_forks_by_id WHERE id = launches.id
       ),
       'time_during_start_process', (
-        SELECT AndroidStartupMetric_Slice('dur_ns', dur)
+        SELECT AndroidStartupMetric_Slice(
+          'dur_ns', dur,
+          'dur_ms', dur / 1e6
+        )
         FROM zygote_forks_by_id WHERE id = launches.id
-      ),
-      'other_process_to_activity_cpu_ratio', (
-        SELECT cpu_ratio FROM launch_cpu WHERE launch_id = launches.id
+      )
+    ),
+    'hsc', (
+      SELECT AndroidStartupMetric_HscMetrics(
+        'full_startup', (
+          SELECT AndroidStartupMetric_Slice(
+            'dur_ns', dur,
+            'dur_ms', dur / 1e6
+          )
+          FROM hsc_based_startup_times WHERE id = launches.id
+        )
       )
     )
   ) as startup
 FROM launches;
 
+CREATE VIEW android_startup_annotations AS
+SELECT
+  'slice' as track_type,
+  'Android App Startups' as track_name,
+  l.ts as ts,
+  l.dur as dur,
+  l.package as slice_name
+FROM launches l;
+
 CREATE VIEW android_startup_output AS
 SELECT
   AndroidStartupMetric(
diff --git a/src/trace_processor/metrics/android/android_startup_cpu.sql b/src/trace_processor/metrics/android/android_startup_cpu.sql
deleted file mode 100644
index f71282e..0000000
--- a/src/trace_processor/metrics/android/android_startup_cpu.sql
+++ /dev/null
@@ -1,51 +0,0 @@
---
--- Copyright 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
---
---     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.
---
-
--- Sched view per process
-CREATE VIEW per_process_cpu AS
-SELECT process.upid AS upid, ts, dur
-FROM sched
-JOIN thread USING(utid)
-JOIN process USING(upid);
-
--- CPU usage during the activity launch.
-CREATE TABLE launch_cpu_per_process_type AS
-SELECT
-  id AS launch_id,
-  per_process_cpu.upid IN (
-    SELECT upid FROM launch_processes AS lp WHERE lp.launch_id = launches.id
-  ) AS is_launch_process,
-  SUM(per_process_cpu.dur) AS dur
-FROM launches
-JOIN per_process_cpu ON (
-  per_process_cpu.ts BETWEEN launches.ts AND launches.ts + launches.dur)
-GROUP BY 1, 2;
-
-CREATE VIEW launch_cpu AS
-SELECT
-  launch_id,
-  other_process.dur AS other_process_dur,
-  launch_process.dur AS launch_process_dur,
-  1.0 * IFNULL(other_process.dur, 0) / launch_process.dur AS cpu_ratio
-FROM (
-  SELECT * FROM launch_cpu_per_process_type
-  WHERE is_launch_process = 1
-) AS launch_process
-LEFT JOIN (
-  SELECT * FROM launch_cpu_per_process_type
-  WHERE is_launch_process = 0
-) AS other_process
-USING (launch_id);
diff --git a/src/trace_processor/metrics/android/android_startup_launches.sql b/src/trace_processor/metrics/android/android_startup_launches.sql
index 9b8caf1..1fcb059 100644
--- a/src/trace_processor/metrics/android/android_startup_launches.sql
+++ b/src/trace_processor/metrics/android/android_startup_launches.sql
@@ -14,32 +14,20 @@
 -- limitations under the License.
 --
 
--- Helper to optimize the query for launching events
--- TODO(b/132771327): remove when fixed
-CREATE TABLE launching_events_helper AS
-SELECT
-  arg_set_id,
-  STR_SPLIT(STR_SPLIT(args.string_value, "|", 2), ": ", 1) package_name,
-  STR_SPLIT(args.string_value, "|", 0) type
-FROM args
-WHERE string_value LIKE '%|launching: %';
-
--- TODO: Replace with proper async slice once available
 -- The start of the launching event corresponds to the end of the AM handling
 -- the startActivity intent, whereas the end corresponds to the first frame drawn.
 -- Only successful app launches have a launching event.
 CREATE TABLE launching_events AS
 SELECT
   ts,
-  package_name,
-  launching_events_helper.type
-FROM raw
-CROSS JOIN launching_events_helper
-JOIN thread USING(utid)
+  dur,
+  ts + dur AS ts_end,
+  STR_SPLIT(s.name, ": ", 1) AS package_name
+FROM slice s
+JOIN process_track t ON s.track_id = t.id
 JOIN process USING(upid)
-WHERE raw.arg_set_id = launching_events_helper.arg_set_id
-AND raw.name = 'print'
-AND process.name = 'system_server';
+WHERE s.name LIKE 'launching: %'
+AND (process.name IS NULL OR process.name = 'system_server');
 
 -- Marks the beginning of the trace and is equivalent to when the statsd launch
 -- logging begins.
@@ -74,9 +62,7 @@
 WHERE 1 = (
   SELECT COUNT(1)
   FROM launching_events
-  WHERE TRUE
-    AND type = 'S'
-    AND ts BETWEEN spans.ts AND spans.ts + spans.dur);
+  WHERE launching_events.ts BETWEEN spans.ts AND spans.ts + spans.dur);
 
 -- All activity launches in the trace, keyed by ID.
 CREATE TABLE launches(
@@ -91,15 +77,14 @@
 INSERT INTO launches
 SELECT
   lpart.ts AS ts,
-  finish_event.ts AS ts_end,
-  finish_event.ts - lpart.ts AS dur,
+  launching_events.ts_end AS ts_end,
+  launching_events.ts_end - lpart.ts AS dur,
   lpart.id AS id,
-  start_event.package_name AS package
+  package_name AS package
 FROM launch_partitions AS lpart
-JOIN (SELECT * FROM launching_events WHERE type = 'S') AS start_event
-  ON start_event.ts BETWEEN lpart.ts AND lpart.ts + lpart.dur
-JOIN (SELECT * FROM launching_events WHERE type = 'F') AS finish_event
-  ON finish_event.ts BETWEEN lpart.ts AND lpart.ts + lpart.dur
+JOIN launching_events ON
+  (launching_events.ts BETWEEN lpart.ts AND lpart.ts + lpart.dur) AND
+  (launching_events.ts_end BETWEEN lpart.ts AND lpart.ts + lpart.dur)
 JOIN activity_intent_launch_successful AS successful
   ON successful.ts BETWEEN lpart.ts AND lpart.ts + lpart.dur;
 
diff --git a/src/trace_processor/metrics/android/android_task_names.sql b/src/trace_processor/metrics/android/android_task_names.sql
new file mode 100644
index 0000000..be88267
--- /dev/null
+++ b/src/trace_processor/metrics/android/android_task_names.sql
@@ -0,0 +1,52 @@
+--
+-- 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 RUN_METRIC('android/process_metadata.sql');
+
+CREATE VIEW IF NOT EXISTS android_task_names_output AS
+WITH
+-- Process to thread name
+threads_by_upid AS (
+  SELECT
+    upid,
+    RepeatedField(name) AS thread_names
+  FROM thread
+  WHERE name IS NOT NULL
+  GROUP BY 1
+),
+upid_packages AS (
+  SELECT
+    upid,
+    RepeatedField(package_list.package_name) AS packages
+  FROM process
+  JOIN package_list ON process.android_appid = package_list.uid
+  GROUP BY 1
+)
+SELECT AndroidTaskNames(
+  'process', RepeatedField(
+    AndroidTaskNames_Process(
+      'pid', p.pid,
+      'process_name', p.name,
+      'thread_name', threads_by_upid.thread_names,
+      'uid', p.uid,
+      'uid_package_name', upid_packages.packages
+    )
+  )
+)
+FROM process p
+LEFT JOIN threads_by_upid USING (upid)
+LEFT JOIN upid_packages USING (upid)
+WHERE upid != 0;
diff --git a/src/trace_processor/metrics/android/android_thread_time_in_state.sql b/src/trace_processor/metrics/android/android_thread_time_in_state.sql
new file mode 100644
index 0000000..1cb65c8
--- /dev/null
+++ b/src/trace_processor/metrics/android/android_thread_time_in_state.sql
@@ -0,0 +1,104 @@
+--
+-- 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 RUN_METRIC('android/cpu_info.sql');
+SELECT RUN_METRIC('android/process_metadata.sql');
+
+CREATE VIEW android_thread_time_in_state_tracks AS
+SELECT
+  id AS track_id,
+  utid,
+  cast(REPLACE(REPLACE(name, '.time_in_state', ''), 'cpu', '') AS int) AS cpu
+FROM thread_counter_track
+WHERE name LIKE "cpu%.time_in_state";
+
+CREATE TABLE android_thread_time_in_state_counters AS
+SELECT
+  utid,
+  (
+    SELECT
+      CASE
+        WHEN layout = 'big_little_bigger' AND cpu < 4 THEN 'little'
+        WHEN layout = 'big_little_bigger' AND cpu < 7 THEN 'big'
+        WHEN layout = 'big_little_bigger' AND cpu = 7 THEN 'bigger'
+        WHEN layout = 'big_little' AND cpu < 4 THEN 'little'
+        WHEN layout = 'big_little' AND cpu < 8 THEN 'big'
+        ELSE 'unknown'
+      END
+    FROM core_layout_type
+  ) AS core_type,
+  CAST((MAX(counter.value) - MIN(counter.value)) AS int) runtime_ms
+FROM counter
+JOIN android_thread_time_in_state_tracks USING (track_id)
+GROUP BY 1, 2
+HAVING runtime_ms > 0;
+
+CREATE VIEW android_thread_time_in_state_thread_metrics AS
+SELECT
+  utid,
+  RepeatedField(AndroidThreadTimeInStateMetric_MetricsByCoreType(
+    'core_type', core_type,
+    'runtime_ms', runtime_ms
+  )) metrics
+FROM android_thread_time_in_state_counters
+GROUP BY 1;
+
+CREATE VIEW android_thread_time_in_state_threads AS
+SELECT
+  upid,
+  RepeatedField(AndroidThreadTimeInStateMetric_Thread(
+    'name', thread.name,
+    'metrics_by_core_type', android_thread_time_in_state_thread_metrics.metrics
+  )) threads
+FROM thread
+JOIN android_thread_time_in_state_thread_metrics USING (utid)
+GROUP BY 1;
+
+CREATE VIEW android_thread_time_in_state_process_metrics AS
+WITH process_counters AS (
+  SELECT
+    upid,
+    core_type,
+    SUM(runtime_ms) runtime_ms
+  FROM android_thread_time_in_state_counters
+  JOIN thread USING (utid)
+  GROUP BY 1, 2
+)
+SELECT
+  upid,
+  RepeatedField(AndroidThreadTimeInStateMetric_MetricsByCoreType(
+    'core_type', core_type,
+    'runtime_ms', runtime_ms
+  )) metrics
+FROM process_counters
+GROUP BY 1;
+
+CREATE VIEW android_thread_time_in_state_output AS
+SELECT AndroidThreadTimeInStateMetric(
+  'processes', (
+    SELECT
+      RepeatedField(AndroidThreadTimeInStateMetric_Process(
+        'metadata', metadata,
+        'metrics_by_core_type',
+            android_thread_time_in_state_process_metrics.metrics,
+        'threads', android_thread_time_in_state_threads.threads
+      ))
+    FROM process
+    JOIN process_metadata USING (upid)
+    JOIN android_thread_time_in_state_process_metrics USING (upid)
+    JOIN android_thread_time_in_state_threads USING (upid)
+  )
+);
diff --git a/src/trace_processor/metrics/android/cpu_info.sql b/src/trace_processor/metrics/android/cpu_info.sql
new file mode 100644
index 0000000..00c887c
--- /dev/null
+++ b/src/trace_processor/metrics/android/cpu_info.sql
@@ -0,0 +1,44 @@
+--
+-- 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.
+--
+
+CREATE VIEW IF NOT EXISTS core_layout_mapping AS
+SELECT
+  CASE
+    WHEN (
+      str_value LIKE '%flame%' OR
+      str_value LIKE '%coral%'
+    ) THEN 'big_little_bigger'
+    WHEN (
+      str_value LIKE '%taimen%' OR
+      str_value LIKE '%walleye%' OR
+      str_value LIKE '%bonito%' OR
+      str_value LIKE '%sargo%' OR
+      str_value LIKE '%blueline%' OR
+      str_value LIKE '%crosshatch%'
+    ) THEN 'big_little'
+    ELSE 'unknown'
+  END AS layout
+FROM metadata
+WHERE name = 'android_build_fingerprint';
+
+CREATE TABLE IF NOT EXISTS core_layout_type AS
+SELECT *
+FROM (
+  SELECT layout from core_layout_mapping
+  UNION
+  SELECT 'unknown'
+)
+LIMIT 1;
diff --git a/src/trace_processor/metrics/android/display_metrics.sql b/src/trace_processor/metrics/android/display_metrics.sql
new file mode 100644
index 0000000..321ef4d
--- /dev/null
+++ b/src/trace_processor/metrics/android/display_metrics.sql
@@ -0,0 +1,33 @@
+--
+-- 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.
+--
+CREATE VIEW same_frame AS
+SELECT COUNT(name) AS total_duplicate_frames
+FROM counters
+WHERE name='SAME_FRAME'
+AND value=1;
+
+CREATE VIEW duplicate_frames_logged AS
+SELECT CASE WHEN COUNT(name) > 0 THEN 1 ELSE 0 END AS logs_found
+FROM counters
+WHERE name='SAME_FRAME' AND value=0;
+
+CREATE VIEW display_metrics_output AS
+SELECT AndroidDisplayMetrics(
+    'total_duplicate_frames', (SELECT total_duplicate_frames
+                            FROM same_frame),
+    'duplicate_frames_logged', (SELECT logs_found
+                            FROM duplicate_frames_logged)
+);
\ No newline at end of file
diff --git a/src/trace_processor/metrics/android/hsc_startups.sql b/src/trace_processor/metrics/android/hsc_startups.sql
new file mode 100644
index 0000000..330aa63
--- /dev/null
+++ b/src/trace_processor/metrics/android/hsc_startups.sql
@@ -0,0 +1,60 @@
+--
+-- 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.
+--
+
+-- Must be invoked after populating launches table in android_startup.
+CREATE VIEW frame_times AS
+SELECT
+    slices.ts AS ts,
+    slices.ts + slices.dur AS ts_end,
+    thread.name AS name,
+    ROW_NUMBER() OVER(PARTITION BY thread.name ORDER BY ts ASC) as frame_number
+FROM slices
+INNER JOIN thread_track on slices.track_id = thread_track.id
+INNER JOIN thread USING(utid)
+WHERE slices.name="Choreographer#doFrame";
+
+CREATE VIEW functions AS
+SELECT
+    slices.ts as ts,
+    slices.dur as dur,
+    thread.name as process_name,
+    slices.name as function_name
+FROM slices
+INNER JOIN process_track on slices.track_id = process_track.id
+INNER JOIN thread USING(upid);
+
+CREATE TABLE hsc_based_startup_times(package STRING, id INT, dur_ns INT);
+
+-- Netflix
+INSERT INTO hsc_based_startup_times
+SELECT
+    launches.package as package,
+    launches.id as id,
+    frame_times.ts_end - launches.ts as ts_total
+FROM frame_times
+INNER JOIN launches on launches.package LIKE '%' || frame_times.name || '%'
+WHERE frame_times.ts < (SELECT ts FROM functions WHERE function_name LIKE "animator%" AND process_name LIKE "%lix.mediaclient" ORDER BY ts LIMIT 1)
+ORDER BY ts_total LIMIT 1;
+
+-- Maps
+INSERT INTO hsc_based_startup_times
+SELECT
+    launches.package as package,
+    launches.id as id,
+    frame_times.ts_end - launches.ts as ts_total
+FROM frame_times
+INNER JOIN launches on launches.package LIKE '%' || frame_times.name || '%'
+WHERE frame_times.frame_number=1 AND frame_times.name LIKE "%maps%";
diff --git a/src/trace_processor/metrics/android/java_heap_histogram.sql b/src/trace_processor/metrics/android/java_heap_histogram.sql
new file mode 100644
index 0000000..a8d9b5f
--- /dev/null
+++ b/src/trace_processor/metrics/android/java_heap_histogram.sql
@@ -0,0 +1,62 @@
+--
+-- 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 RUN_METRIC('android/process_metadata.sql');
+
+CREATE VIEW IF NOT EXISTS java_heap_histogram_output AS
+WITH
+-- Base histogram table
+heap_obj_histograms AS (
+  SELECT
+    o.upid,
+    o.graph_sample_ts,
+    IFNULL(c.deobfuscated_name, c.name) AS type_name,
+    COUNT(1) obj_count,
+    SUM(CASE o.reachable WHEN TRUE THEN 1 ELSE 0 END) reachable_obj_count
+  FROM heap_graph_object o JOIN heap_graph_class c ON o.type_id = c.id
+  GROUP BY 1, 2, 3
+),
+-- Group by to build the repeated field by upid, ts
+heap_obj_histogram_count_protos AS (
+  SELECT
+    upid,
+    graph_sample_ts,
+    RepeatedField(JavaHeapHistogram_TypeCount(
+      'type_name', type_name,
+      'obj_count', obj_count,
+      'reachable_obj_count', reachable_obj_count
+    )) AS count_protos
+  FROM heap_obj_histograms
+  GROUP BY 1, 2
+),
+-- Group by to build the repeated field by upid
+heap_obj_histogram_sample_protos AS (
+  SELECT
+    upid,
+    RepeatedField(JavaHeapHistogram_Sample(
+      'ts', graph_sample_ts,
+      'type_count', count_protos
+    )) AS sample_protos
+  FROM heap_obj_histogram_count_protos
+  GROUP BY 1
+)
+SELECT JavaHeapHistogram(
+  'instance_stats', RepeatedField(JavaHeapHistogram_InstanceStats(
+    'upid', upid,
+    'process', process_metadata.metadata,
+    'samples', sample_protos
+  )))
+FROM heap_obj_histogram_sample_protos JOIN process_metadata USING (upid);
diff --git a/src/trace_processor/metrics/android/java_heap_stats.sql b/src/trace_processor/metrics/android/java_heap_stats.sql
index e5d80b2..d353448 100644
--- a/src/trace_processor/metrics/android/java_heap_stats.sql
+++ b/src/trace_processor/metrics/android/java_heap_stats.sql
@@ -15,46 +15,45 @@
 --
 
 SELECT RUN_METRIC('android/process_metadata.sql');
-
-CREATE VIEW total_size_samples AS
-SELECT upid, graph_sample_ts, SUM(self_size) AS total_size
-FROM heap_graph_object
-GROUP BY 1, 2;
-
-CREATE VIEW total_reachable_size_samples AS
-SELECT upid, graph_sample_ts, SUM(self_size) AS total_reachable_size
-FROM heap_graph_object
-WHERE reachable = TRUE
-GROUP BY 1, 2;
-
-CREATE TABLE heap_graph_samples AS
-SELECT upid, graph_sample_ts, total_size, total_reachable_size
-FROM total_size_samples JOIN total_reachable_size_samples
-USING (upid, graph_sample_ts);
-
-CREATE VIEW heap_graph_sample_protos AS
-SELECT
-  upid,
-  JavaHeapStats_Sample(
-    'ts', graph_sample_ts,
-    'heap_size', total_size,
-    'reachable_heap_size', total_reachable_size
-  ) sample_proto
-FROM heap_graph_samples;
-
-CREATE TABLE heap_graph_instance_stats AS
-SELECT
-  upid,
-  process_metadata.metadata AS process_metadata,
-  RepeatedField(sample_proto) AS sample_protos
-FROM heap_graph_sample_protos JOIN process_metadata USING (upid)
-GROUP BY 1, 2;
+SELECT RUN_METRIC('android/process_mem.sql');
 
 CREATE VIEW java_heap_stats_output AS
+WITH
+-- Base view
+base_stats AS (
+  SELECT
+    upid,
+    graph_sample_ts,
+    SUM(self_size) AS total_size,
+    COUNT(1) AS total_obj_count,
+    SUM(CASE reachable WHEN TRUE THEN self_size ELSE 0 END) AS reachable_size,
+    SUM(CASE reachable WHEN TRUE THEN 1 ELSE 0 END) AS reachable_obj_count
+  FROM heap_graph_object
+  GROUP BY 1, 2
+),
+-- Group by upid
+heap_graph_sample_protos AS (
+  SELECT
+    base_stats.upid,
+    RepeatedField(JavaHeapStats_Sample(
+      'ts', graph_sample_ts,
+      'heap_size', total_size,
+      'obj_count', total_obj_count,
+      'reachable_heap_size', reachable_size,
+      'reachable_obj_count', reachable_obj_count,
+      'anon_rss_and_swap_size', CAST(anon_and_swap_val AS INTEGER)
+    )) sample_protos
+  FROM base_stats
+  LEFT JOIN anon_and_swap_span ON
+    base_stats.upid = anon_and_swap_span.upid
+    AND anon_and_swap_span.ts <= base_stats.graph_sample_ts
+    AND base_stats.graph_sample_ts < anon_and_swap_span.ts + anon_and_swap_span.dur
+  GROUP BY 1
+)
 SELECT JavaHeapStats(
   'instance_stats', RepeatedField(JavaHeapStats_InstanceStats(
     'upid', upid,
-    'process', process_metadata,
+    'process', process_metadata.metadata,
     'samples', sample_protos
   )))
-FROM heap_graph_instance_stats;
+FROM heap_graph_sample_protos JOIN process_metadata USING (upid);
diff --git a/src/trace_processor/metrics/android/process_metadata.sql b/src/trace_processor/metrics/android/process_metadata.sql
index 2dbc0b4..401948d 100644
--- a/src/trace_processor/metrics/android/process_metadata.sql
+++ b/src/trace_processor/metrics/android/process_metadata.sql
@@ -14,15 +14,6 @@
 -- limitations under the License.
 --
 
-SELECT RUN_METRIC('android/android_package_list.sql');
-
--- Create a view of the process with the app ID as defined in
--- //frameworks/base/core/java/android/os/UserHandle.java
--- TODO: move this to the trace processor once the table migration is complete.
-CREATE VIEW IF NOT EXISTS proc_uid AS
-SELECT upid, name, uid % 100000 AS uid
-FROM process;
-
 DROP TABLE IF EXISTS uid_package_count;
 
 CREATE TABLE uid_package_count AS
@@ -34,28 +25,26 @@
 
 CREATE TABLE process_metadata_table AS
 SELECT
-  proc_uid.upid,
-  proc_uid.name AS process_name,
-  proc_uid.uid,
+  process.upid,
+  process.name AS process_name,
+  process.android_appid AS uid,
   CASE WHEN uid_package_count.cnt > 1 THEN TRUE ELSE NULL END AS shared_uid,
   plist.package_name,
   plist.version_code,
   plist.debuggable
-FROM proc_uid
-LEFT JOIN uid_package_count USING (uid)
+FROM process
+LEFT JOIN uid_package_count ON process.android_appid = uid_package_count.uid
 LEFT JOIN package_list plist
 ON (
-  proc_uid.uid = plist.uid
+  process.android_appid = plist.uid
   AND uid_package_count.uid = plist.uid
   AND (
     -- unique match
     uid_package_count.cnt = 1
     -- or process name starts with the package name
-    OR proc_uid.name LIKE plist.package_name || '%')
+    OR process.name LIKE plist.package_name || '%')
   );
 
-DROP VIEW IF EXISTS process_metadata;
-
 CREATE VIEW IF NOT EXISTS process_metadata AS
 WITH upid_packages AS (
   SELECT
@@ -65,8 +54,8 @@
     'apk_version_code', package_list.version_code,
     'debuggable', package_list.debuggable
   )) packages_for_uid
-  FROM proc_uid
-  JOIN package_list USING (uid)
+  FROM process
+  JOIN package_list ON process.android_appid = package_list.uid
   GROUP BY upid
 )
 SELECT
diff --git a/src/trace_processor/metrics/android/unmapped_java_symbols.sql b/src/trace_processor/metrics/android/unmapped_java_symbols.sql
index d13d948..1b7f829 100644
--- a/src/trace_processor/metrics/android/unmapped_java_symbols.sql
+++ b/src/trace_processor/metrics/android/unmapped_java_symbols.sql
@@ -19,11 +19,23 @@
 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
-  AND INSTR(type_name, '.') = 0
-  AND RTRIM(type_name, '[]') NOT IN ('byte', 'char', 'short', 'int', 'long', 'boolean', 'float', 'double')
+  FROM (
+    SELECT
+      upid,
+      RTRIM(REPLACE(c.name, 'java.lang.Class<', ''), '[]>') AS type_name
+    FROM heap_graph_object o JOIN heap_graph_class c ON o.type_id = c.id
+    WHERE c.deobfuscated_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
@@ -31,21 +43,32 @@
 
 CREATE TABLE IF NOT EXISTS fields_per_upid AS
 WITH distinct_unmapped_field_names AS (
-  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 '%.%.%'
+  SELECT DISTINCT o.upid, field_type_name, field_name
+    FROM heap_graph_object o JOIN heap_graph_class c ON o.type_id = c.id
+           JOIN heap_graph_reference USING (reference_set_id)
+  WHERE c.deobfuscated_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
+SELECT upid, RepeatedField(
+  UnmappedJavaSymbols_Field(
+    'field_name', field_name,
+    'field_type_name', field_type_name)) AS fields
 FROM distinct_unmapped_field_names GROUP BY 1;
 
 CREATE VIEW IF NOT EXISTS java_symbols_per_process AS
 SELECT UnmappedJavaSymbols_ProcessSymbols(
   'process_metadata', metadata,
   'type_name', types,
-  'field_name', fields
+  'field', fields
 ) types
 FROM types_per_upid
 JOIN process_metadata USING (upid)
diff --git a/src/trace_processor/metrics/custom_options.descriptor.h b/src/trace_processor/metrics/custom_options.descriptor.h
index 3829d74..c7edf8d 100644
--- a/src/trace_processor/metrics/custom_options.descriptor.h
+++ b/src/trace_processor/metrics/custom_options.descriptor.h
@@ -25,7 +25,7 @@
 // This file was autogenerated by tools/gen_binary_descriptors. Do not edit.
 
 // SHA1(tools/gen_binary_descriptors)
-// d6628b15181dba5287e35b56b966b39ea93d42b1
+// 3df80477da2ea38cc659967487b37051a154bb69
 // SHA1(protos/perfetto/metrics/custom_options.proto)
 // 074c971d85f72345988fb3279345cd6b3dabde9c
 
diff --git a/src/trace_processor/metrics/metrics.cc b/src/trace_processor/metrics/metrics.cc
index 015e7d9..26c2e26 100644
--- a/src/trace_processor/metrics/metrics.cc
+++ b/src/trace_processor/metrics/metrics.cc
@@ -225,17 +225,22 @@
   if (field.is_repeated() && !is_inside_repeated)
     return AppendRepeated(field, ptr, size);
 
-  switch (field.type()) {
-    case FieldDescriptorProto::TYPE_MESSAGE:
-      return AppendSingleMessage(field, ptr, size);
-    default: {
-      return util::ErrStatus(
-          "Tried to write value of type bytes into field %s (in proto type %s) "
-          "which has type %d",
-          field.name().c_str(), descriptor_->full_name().c_str(), field.type());
-    }
+  if (field.type() == FieldDescriptorProto::TYPE_MESSAGE)
+    return AppendSingleMessage(field, ptr, size);
+
+  if (size == 0) {
+    return util::ErrStatus(
+        "Tried to write null value into field %s (in proto type %s). "
+        "Nulls are only supported for message protos; all other types should"
+        "ensure that nulls are not passed to proto builder functions by using"
+        "the SQLite IFNULL/COALESCE functions.",
+        field.name().c_str(), descriptor_->full_name().c_str());
   }
-  PERFETTO_FATAL("For GCC");
+
+  return util::ErrStatus(
+      "Tried to write value of type bytes into field %s (in proto type %s) "
+      "which has type %d",
+      field.name().c_str(), descriptor_->full_name().c_str(), field.type());
 }
 
 util::Status ProtoBuilder::AppendSingleMessage(const FieldDescriptor& field,
diff --git a/src/trace_processor/metrics/metrics.descriptor.h b/src/trace_processor/metrics/metrics.descriptor.h
index f703d36..c5c941a 100644
--- a/src/trace_processor/metrics/metrics.descriptor.h
+++ b/src/trace_processor/metrics/metrics.descriptor.h
@@ -25,23 +25,23 @@
 // This file was autogenerated by tools/gen_binary_descriptors. Do not edit.
 
 // SHA1(tools/gen_binary_descriptors)
-// d6628b15181dba5287e35b56b966b39ea93d42b1
+// 3df80477da2ea38cc659967487b37051a154bb69
 // SHA1(protos/perfetto/metrics/metrics.proto)
-// 29445577a551e8035ed8e99128ae38062fa77ee0
+// ca2635e5a142683e6b1f5599302625117db0b03e
 
 // 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, 14060> kMetricsDescriptor{
-    {0x0a, 0x94, 0x03, 0x0a, 0x31, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f,
+constexpr std::array<uint8_t, 18017> kMetricsDescriptor{
+    {0x0a, 0x9f, 0x05, 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,
      0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66,
      0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22,
-     0xcd, 0x02, 0x0a, 0x14, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x42,
+     0xd8, 0x04, 0x0a, 0x14, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x42,
      0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
      0x12, 0x60, 0x0a, 0x10, 0x62, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x5f,
      0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03,
@@ -51,105 +51,139 @@
      0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72,
      0x79, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x52, 0x0f, 0x62,
      0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65,
-     0x72, 0x73, 0x1a, 0xd2, 0x01, 0x0a, 0x0f, 0x42, 0x61, 0x74, 0x74, 0x65,
-     0x72, 0x79, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x21,
-     0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f,
-     0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x69,
-     0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4e, 0x73, 0x12, 0x2c, 0x0a,
-     0x12, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e,
-     0x74, 0x65, 0x72, 0x5f, 0x75, 0x61, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28,
-     0x01, 0x52, 0x10, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x43, 0x6f, 0x75,
-     0x6e, 0x74, 0x65, 0x72, 0x55, 0x61, 0x68, 0x12, 0x29, 0x0a, 0x10, 0x63,
-     0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x70, 0x65, 0x72, 0x63,
-     0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0f, 0x63,
-     0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x50, 0x65, 0x72, 0x63, 0x65,
-     0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e,
-     0x74, 0x5f, 0x75, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09,
-     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, 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, 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,
+     0x72, 0x73, 0x12, 0x66, 0x0a, 0x12, 0x62, 0x61, 0x74, 0x74, 0x65, 0x72,
+     0x79, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73,
+     0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x37, 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, 0x2e, 0x42, 0x61,
+     0x74, 0x74, 0x65, 0x72, 0x79, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61,
+     0x74, 0x65, 0x73, 0x52, 0x11, 0x62, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79,
+     0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x1a, 0xd2,
+     0x01, 0x0a, 0x0f, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x43, 0x6f,
+     0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69,
+     0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, 0x18, 0x01,
+     0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
+     0x61, 0x6d, 0x70, 0x4e, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x68, 0x61,
+     0x72, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f,
+     0x75, 0x61, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x63,
+     0x68, 0x61, 0x72, 0x67, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72,
+     0x55, 0x61, 0x68, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x61, 0x70, 0x61, 0x63,
+     0x69, 0x74, 0x79, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x18,
+     0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0f, 0x63, 0x61, 0x70, 0x61, 0x63,
+     0x69, 0x74, 0x79, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x12, 0x1d,
+     0x0a, 0x0a, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x75, 0x61,
+     0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 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, 0x1a, 0xa0, 0x01, 0x0a, 0x11,
+     0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x41, 0x67, 0x67, 0x72, 0x65,
+     0x67, 0x61, 0x74, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x13, 0x74, 0x6f, 0x74,
+     0x61, 0x6c, 0x5f, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x5f, 0x6f, 0x66,
+     0x66, 0x5f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10,
+     0x74, 0x6f, 0x74, 0x61, 0x6c, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x4f,
+     0x66, 0x66, 0x4e, 0x73, 0x12, 0x2b, 0x0a, 0x12, 0x74, 0x6f, 0x74, 0x61,
+     0x6c, 0x5f, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x5f, 0x6f, 0x6e, 0x5f,
+     0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x74, 0x6f,
+     0x74, 0x61, 0x6c, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x4f, 0x6e, 0x4e,
+     0x73, 0x12, 0x2f, 0x0a, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x73,
+     0x63, 0x72, 0x65, 0x65, 0x6e, 0x5f, 0x64, 0x6f, 0x7a, 0x65, 0x5f, 0x6e,
+     0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x74, 0x6f, 0x74,
+     0x61, 0x6c, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x44, 0x6f, 0x7a, 0x65,
+     0x4e, 0x73, 0x0a, 0xd4, 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, 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, 0x8e, 0x08, 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, 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, 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,
+     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, 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,
+     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, 0xb9, 0x02, 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, 0x06, 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, 0x12, 0x3e, 0x0a, 0x04, 0x63, 0x6f,
+     0x72, 0x65, 0x18, 0x07, 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, 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,
@@ -303,20 +337,20 @@
      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, 0x0a, 0xab, 0x02, 0x0a, 0x30, 0x70,
+     0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x0a, 0xe0, 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,
+     0x6f, 0x74, 0x6f, 0x73, 0x22, 0x9a, 0x02, 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,
+     0x06, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x1a, 0xc3, 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,
@@ -328,885 +362,1181 @@
      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,
-     0x63, 0x65, 0x73, 0x73, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
-     0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72,
-     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-     0x22, 0xf4, 0x02, 0x0a, 0x16, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
-     0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x65, 0x74, 0x61, 0x64,
-     0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
-     0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
-     0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03,
-     0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x49, 0x0a, 0x07, 0x70, 0x61, 0x63,
-     0x6b, 0x61, 0x67, 0x65, 0x18, 0x07, 0x20, 0x01, 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,
-     0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x65, 0x74, 0x61, 0x64,
-     0x61, 0x74, 0x61, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52,
-     0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x59, 0x0a, 0x10,
-     0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x5f, 0x66, 0x6f, 0x72,
-     0x5f, 0x75, 0x69, 0x64, 0x18, 0x08, 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,
-     0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x65, 0x74, 0x61, 0x64,
-     0x61, 0x74, 0x61, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52,
-     0x0e, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x46, 0x6f, 0x72,
-     0x55, 0x69, 0x64, 0x1a, 0x76, 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, 0x28, 0x0a, 0x10, 0x61, 0x70, 0x6b, 0x5f, 0x76, 0x65, 0x72,
-     0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20,
-     0x01, 0x28, 0x03, 0x52, 0x0e, 0x61, 0x70, 0x6b, 0x56, 0x65, 0x72, 0x73,
-     0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64,
-     0x65, 0x62, 0x75, 0x67, 0x67, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20,
-     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, 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, 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,
-     0x75, 0x70, 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, 0xde, 0x0d, 0x0a,
-     0x14, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x53, 0x74, 0x61, 0x72,
-     0x74, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x47, 0x0a,
-     0x07, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 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, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x4d,
-     0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75,
-     0x70, 0x52, 0x07, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x1a, 0xe0,
-     0x01, 0x0a, 0x12, 0x54, 0x61, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65,
-     0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x24, 0x0a,
-     0x0e, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x75, 0x72,
-     0x5f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x72,
-     0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x44, 0x75, 0x72, 0x4e, 0x73, 0x12,
-     0x26, 0x0a, 0x0f, 0x72, 0x75, 0x6e, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f,
-     0x64, 0x75, 0x72, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03,
-     0x52, 0x0d, 0x72, 0x75, 0x6e, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x75,
-     0x72, 0x4e, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x75, 0x6e, 0x69, 0x6e, 0x74,
-     0x65, 0x72, 0x72, 0x75, 0x70, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x5f, 0x73,
-     0x6c, 0x65, 0x65, 0x70, 0x5f, 0x64, 0x75, 0x72, 0x5f, 0x6e, 0x73, 0x18,
-     0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x19, 0x75, 0x6e, 0x69, 0x6e, 0x74,
-     0x65, 0x72, 0x72, 0x75, 0x70, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x53, 0x6c,
-     0x65, 0x65, 0x70, 0x44, 0x75, 0x72, 0x4e, 0x73, 0x12, 0x3b, 0x0a, 0x1a,
-     0x69, 0x6e, 0x74, 0x65, 0x72, 0x72, 0x75, 0x70, 0x74, 0x69, 0x62, 0x6c,
-     0x65, 0x5f, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x5f, 0x64, 0x75, 0x72, 0x5f,
-     0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x17, 0x69, 0x6e,
-     0x74, 0x65, 0x72, 0x72, 0x75, 0x70, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x53,
-     0x6c, 0x65, 0x65, 0x70, 0x44, 0x75, 0x72, 0x4e, 0x73, 0x1a, 0x1e, 0x0a,
-     0x05, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x75,
-     0x72, 0x5f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05,
-     0x64, 0x75, 0x72, 0x4e, 0x73, 0x1a, 0xbb, 0x08, 0x0a, 0x0c, 0x54, 0x6f,
-     0x46, 0x69, 0x72, 0x73, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x15,
-     0x0a, 0x06, 0x64, 0x75, 0x72, 0x5f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01,
-     0x28, 0x03, 0x52, 0x05, 0x64, 0x75, 0x72, 0x4e, 0x73, 0x12, 0x72, 0x0a,
-     0x19, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64,
-     0x5f, 0x62, 0x79, 0x5f, 0x74, 0x61, 0x73, 0x6b, 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, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x53, 0x74,
-     0x61, 0x72, 0x74, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e,
-     0x54, 0x61, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x42, 0x72, 0x65,
-     0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x15, 0x6d, 0x61, 0x69, 0x6e,
-     0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x42, 0x79, 0x54, 0x61, 0x73, 0x6b,
-     0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x41, 0x0a, 0x1d, 0x6f, 0x74, 0x68,
-     0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73,
-     0x5f, 0x73, 0x70, 0x61, 0x77, 0x6e, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x75,
-     0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x1a, 0x6f, 0x74,
-     0x68, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73,
-     0x53, 0x70, 0x61, 0x77, 0x6e, 0x65, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74,
-     0x12, 0x5f, 0x0a, 0x15, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x61, 0x63, 0x74,
-     0x69, 0x76, 0x69, 0x74, 0x79, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
-     0x72, 0x18, 0x04, 0x20, 0x01, 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, 0x53, 0x74, 0x61,
-     0x72, 0x74, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x53,
-     0x6c, 0x69, 0x63, 0x65, 0x52, 0x13, 0x74, 0x69, 0x6d, 0x65, 0x41, 0x63,
-     0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65,
-     0x72, 0x12, 0x66, 0x0a, 0x19, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x61, 0x63,
-     0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61,
-     0x64, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 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, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x4d, 0x65, 0x74,
-     0x72, 0x69, 0x63, 0x2e, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x52, 0x16, 0x74,
-     0x69, 0x6d, 0x65, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x54,
-     0x68, 0x72, 0x65, 0x61, 0x64, 0x4d, 0x61, 0x69, 0x6e, 0x12, 0x5f, 0x0a,
-     0x15, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x61,
-     0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06,
-     0x20, 0x01, 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, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75,
-     0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x53, 0x6c, 0x69, 0x63,
-     0x65, 0x52, 0x13, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x69, 0x6e, 0x64, 0x41,
-     0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x5b,
-     0x0a, 0x13, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76,
-     0x69, 0x74, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x07, 0x20,
-     0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x69, 0x7a, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x16,
+     0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f,
+     0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x05,
+     0x20, 0x01, 0x28, 0x01, 0x52, 0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41,
+     0x6c, 0x6c, 0x6f, 0x63, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74, 0x65,
+     0x73, 0x0a, 0xbb, 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,
+     0xf5, 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, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70,
-     0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x53, 0x6c, 0x69, 0x63, 0x65,
-     0x52, 0x11, 0x74, 0x69, 0x6d, 0x65, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69,
-     0x74, 0x79, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x5d, 0x0a, 0x14, 0x74,
-     0x69, 0x6d, 0x65, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79,
-     0x5f, 0x72, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 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, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x4d, 0x65,
-     0x74, 0x72, 0x69, 0x63, 0x2e, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x52, 0x12,
-     0x74, 0x69, 0x6d, 0x65, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79,
-     0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x12, 0x5a, 0x0a, 0x12, 0x74, 0x69,
-     0x6d, 0x65, 0x5f, 0x63, 0x68, 0x6f, 0x72, 0x65, 0x6f, 0x67, 0x72, 0x61,
-     0x70, 0x68, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 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,
-     0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69,
-     0x63, 0x2e, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x52, 0x11, 0x74, 0x69, 0x6d,
-     0x65, 0x43, 0x68, 0x6f, 0x72, 0x65, 0x6f, 0x67, 0x72, 0x61, 0x70, 0x68,
-     0x65, 0x72, 0x12, 0x66, 0x0a, 0x19, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x62,
-     0x65, 0x66, 0x6f, 0x72, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f,
-     0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x18, 0x0a, 0x20, 0x01, 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, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x4d, 0x65,
-     0x74, 0x72, 0x69, 0x63, 0x2e, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x52, 0x16,
-     0x74, 0x69, 0x6d, 0x65, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x53, 0x74,
-     0x61, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x66,
-     0x0a, 0x19, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x64, 0x75, 0x72, 0x69, 0x6e,
-     0x67, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x63,
-     0x65, 0x73, 0x73, 0x18, 0x0b, 0x20, 0x01, 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, 0x53,
-     0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
-     0x2e, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x52, 0x16, 0x74, 0x69, 0x6d, 0x65,
-     0x44, 0x75, 0x72, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x72, 0x74, 0x50,
-     0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x4b, 0x0a, 0x23, 0x6f, 0x74,
-     0x68, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f,
-     0x74, 0x6f, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x5f,
-     0x63, 0x70, 0x75, 0x5f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x18, 0x0c, 0x20,
-     0x01, 0x28, 0x01, 0x52, 0x1e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x50, 0x72,
-     0x6f, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x41, 0x63, 0x74, 0x69, 0x76,
-     0x69, 0x74, 0x79, 0x43, 0x70, 0x75, 0x52, 0x61, 0x74, 0x69, 0x6f, 0x1a,
-     0xbb, 0x02, 0x0a, 0x07, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x12,
-     0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x5f, 0x69,
-     0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x73, 0x74, 0x61,
-     0x72, 0x74, 0x75, 0x70, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61,
-     0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02,
-     0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67,
-     0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f,
-     0x63, 0x65, 0x73, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20,
-     0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
-     0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x7a, 0x79, 0x67, 0x6f,
-     0x74, 0x65, 0x5f, 0x6e, 0x65, 0x77, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65,
-     0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x7a, 0x79,
-     0x67, 0x6f, 0x74, 0x65, 0x4e, 0x65, 0x77, 0x50, 0x72, 0x6f, 0x63, 0x65,
-     0x73, 0x73, 0x12, 0x43, 0x0a, 0x1e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69,
-     0x74, 0x79, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x70,
-     0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74,
-     0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x1b, 0x61, 0x63, 0x74, 0x69,
-     0x76, 0x69, 0x74, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x50,
-     0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12,
-     0x58, 0x0a, 0x0e, 0x74, 0x6f, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f,
-     0x66, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32,
-     0x32, 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, 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, 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,
+     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, 0x12, 0x28, 0x0a, 0x10, 0x6f, 0x6f, 0x6d, 0x5f, 0x76, 0x69, 0x63,
+     0x74, 0x69, 0x6d, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20,
+     0x01, 0x28, 0x05, 0x52, 0x0e, 0x6f, 0x6f, 0x6d, 0x56, 0x69, 0x63, 0x74,
+     0x69, 0x6d, 0x43, 0x6f, 0x75, 0x6e, 0x74, 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, 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,
+     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, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66,
+     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22,
+     0xf4, 0x02, 0x0a, 0x16, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50,
+     0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
+     0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
+     0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10,
+     0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52,
+     0x03, 0x75, 0x69, 0x64, 0x12, 0x49, 0x0a, 0x07, 0x70, 0x61, 0x63, 0x6b,
+     0x61, 0x67, 0x65, 0x18, 0x07, 0x20, 0x01, 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, 0x50,
+     0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
+     0x74, 0x61, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, 0x07,
+     0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x59, 0x0a, 0x10, 0x70,
+     0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x5f, 0x66, 0x6f, 0x72, 0x5f,
+     0x75, 0x69, 0x64, 0x18, 0x08, 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, 0x50,
+     0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
+     0x74, 0x61, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, 0x0e,
+     0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x46, 0x6f, 0x72, 0x55,
+     0x69, 0x64, 0x1a, 0x76, 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, 0x28, 0x0a, 0x10, 0x61, 0x70, 0x6b, 0x5f, 0x76, 0x65, 0x72, 0x73,
+     0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01,
+     0x28, 0x03, 0x52, 0x0e, 0x61, 0x70, 0x6b, 0x56, 0x65, 0x72, 0x73, 0x69,
+     0x6f, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65,
+     0x62, 0x75, 0x67, 0x67, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 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, 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, 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, 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,
+     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, 0xa1, 0x13, 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, 0x75,
+     0x70, 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, 0x9f, 0x12, 0x0a, 0x14, 0x41, 0x6e, 0x64, 0x72,
+     0x6f, 0x69, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x4d, 0x65,
+     0x74, 0x72, 0x69, 0x63, 0x12, 0x47, 0x0a, 0x07, 0x73, 0x74, 0x61, 0x72,
+     0x74, 0x75, 0x70, 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, 0x53,
+     0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
+     0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x52, 0x07, 0x73, 0x74,
+     0x61, 0x72, 0x74, 0x75, 0x70, 0x1a, 0xe0, 0x01, 0x0a, 0x12, 0x54, 0x61,
+     0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x42, 0x72, 0x65, 0x61, 0x6b,
+     0x64, 0x6f, 0x77, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x72, 0x75, 0x6e, 0x6e,
+     0x69, 0x6e, 0x67, 0x5f, 0x64, 0x75, 0x72, 0x5f, 0x6e, 0x73, 0x18, 0x01,
+     0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e,
+     0x67, 0x44, 0x75, 0x72, 0x4e, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x72, 0x75,
+     0x6e, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x64, 0x75, 0x72, 0x5f, 0x6e,
+     0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x72, 0x75, 0x6e,
+     0x6e, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x75, 0x72, 0x4e, 0x73, 0x12, 0x3f,
+     0x0a, 0x1c, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x72, 0x75, 0x70,
+     0x74, 0x69, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x5f,
+     0x64, 0x75, 0x72, 0x5f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03,
+     0x52, 0x19, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x72, 0x75, 0x70,
+     0x74, 0x69, 0x62, 0x6c, 0x65, 0x53, 0x6c, 0x65, 0x65, 0x70, 0x44, 0x75,
+     0x72, 0x4e, 0x73, 0x12, 0x3b, 0x0a, 0x1a, 0x69, 0x6e, 0x74, 0x65, 0x72,
+     0x72, 0x75, 0x70, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x6c, 0x65,
+     0x65, 0x70, 0x5f, 0x64, 0x75, 0x72, 0x5f, 0x6e, 0x73, 0x18, 0x04, 0x20,
+     0x01, 0x28, 0x03, 0x52, 0x17, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x72, 0x75,
+     0x70, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x53, 0x6c, 0x65, 0x65, 0x70, 0x44,
+     0x75, 0x72, 0x4e, 0x73, 0x1a, 0x35, 0x0a, 0x05, 0x53, 0x6c, 0x69, 0x63,
+     0x65, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x75, 0x72, 0x5f, 0x6e, 0x73, 0x18,
+     0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x64, 0x75, 0x72, 0x4e, 0x73,
+     0x12, 0x15, 0x0a, 0x06, 0x64, 0x75, 0x72, 0x5f, 0x6d, 0x73, 0x18, 0x02,
+     0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x64, 0x75, 0x72, 0x4d, 0x73, 0x1a,
+     0x80, 0x0b, 0x0a, 0x0c, 0x54, 0x6f, 0x46, 0x69, 0x72, 0x73, 0x74, 0x46,
+     0x72, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x75, 0x72, 0x5f,
+     0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x64, 0x75,
+     0x72, 0x4e, 0x73, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x75, 0x72, 0x5f, 0x6d,
+     0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x64, 0x75, 0x72,
+     0x4d, 0x73, 0x12, 0x72, 0x0a, 0x19, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x74,
+     0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x74, 0x61, 0x73,
+     0x6b, 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, 0x41, 0x6e, 0x64, 0x72,
+     0x6f, 0x69, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x4d, 0x65,
+     0x74, 0x72, 0x69, 0x63, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x53, 0x74, 0x61,
+     0x74, 0x65, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x52,
+     0x15, 0x6d, 0x61, 0x69, 0x6e, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x42,
+     0x79, 0x54, 0x61, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x41,
+     0x0a, 0x1d, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x63,
+     0x65, 0x73, 0x73, 0x65, 0x73, 0x5f, 0x73, 0x70, 0x61, 0x77, 0x6e, 0x65,
+     0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28,
+     0x0d, 0x52, 0x1a, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x63,
+     0x65, 0x73, 0x73, 0x65, 0x73, 0x53, 0x70, 0x61, 0x77, 0x6e, 0x65, 0x64,
+     0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x5f, 0x0a, 0x15, 0x74, 0x69, 0x6d,
+     0x65, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x5f, 0x6d,
+     0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 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, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x4d, 0x65, 0x74,
+     0x72, 0x69, 0x63, 0x2e, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x52, 0x13, 0x74,
+     0x69, 0x6d, 0x65, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x4d,
+     0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, 0x66, 0x0a, 0x19, 0x74, 0x69,
+     0x6d, 0x65, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x5f,
+     0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x18,
+     0x05, 0x20, 0x01, 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, 0x53, 0x74, 0x61, 0x72, 0x74,
+     0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x53, 0x6c, 0x69,
+     0x63, 0x65, 0x52, 0x16, 0x74, 0x69, 0x6d, 0x65, 0x41, 0x63, 0x74, 0x69,
+     0x76, 0x69, 0x74, 0x79, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x4d, 0x61,
+     0x69, 0x6e, 0x12, 0x5f, 0x0a, 0x15, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x62,
+     0x69, 0x6e, 0x64, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74,
+     0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 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, 0x53,
+     0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
+     0x2e, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x52, 0x13, 0x74, 0x69, 0x6d, 0x65,
+     0x42, 0x69, 0x6e, 0x64, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74,
+     0x69, 0x6f, 0x6e, 0x12, 0x5b, 0x0a, 0x13, 0x74, 0x69, 0x6d, 0x65, 0x5f,
+     0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x74, 0x61,
+     0x72, 0x74, 0x18, 0x07, 0x20, 0x01, 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, 0x53, 0x74,
+     0x61, 0x72, 0x74, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e,
+     0x53, 0x6c, 0x69, 0x63, 0x65, 0x52, 0x11, 0x74, 0x69, 0x6d, 0x65, 0x41,
+     0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x53, 0x74, 0x61, 0x72, 0x74,
+     0x12, 0x5d, 0x0a, 0x14, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x61, 0x63, 0x74,
+     0x69, 0x76, 0x69, 0x74, 0x79, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6d, 0x65,
+     0x18, 0x08, 0x20, 0x01, 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, 0x53, 0x74, 0x61, 0x72,
+     0x74, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x53, 0x6c,
+     0x69, 0x63, 0x65, 0x52, 0x12, 0x74, 0x69, 0x6d, 0x65, 0x41, 0x63, 0x74,
+     0x69, 0x76, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x12,
+     0x5a, 0x0a, 0x12, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x63, 0x68, 0x6f, 0x72,
+     0x65, 0x6f, 0x67, 0x72, 0x61, 0x70, 0x68, 0x65, 0x72, 0x18, 0x09, 0x20,
+     0x01, 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, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70,
+     0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x53, 0x6c, 0x69, 0x63, 0x65,
+     0x52, 0x11, 0x74, 0x69, 0x6d, 0x65, 0x43, 0x68, 0x6f, 0x72, 0x65, 0x6f,
+     0x67, 0x72, 0x61, 0x70, 0x68, 0x65, 0x72, 0x12, 0x66, 0x0a, 0x19, 0x74,
+     0x69, 0x6d, 0x65, 0x5f, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x5f, 0x73,
+     0x74, 0x61, 0x72, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
+     0x18, 0x0a, 0x20, 0x01, 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, 0x53, 0x74, 0x61, 0x72,
+     0x74, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x53, 0x6c,
+     0x69, 0x63, 0x65, 0x52, 0x16, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x65, 0x66,
+     0x6f, 0x72, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x63,
+     0x65, 0x73, 0x73, 0x12, 0x66, 0x0a, 0x19, 0x74, 0x69, 0x6d, 0x65, 0x5f,
+     0x64, 0x75, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74,
+     0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x18, 0x0b, 0x20, 0x01,
+     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, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x4d,
+     0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x52,
+     0x16, 0x74, 0x69, 0x6d, 0x65, 0x44, 0x75, 0x72, 0x69, 0x6e, 0x67, 0x53,
+     0x74, 0x61, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12,
+     0x4d, 0x0a, 0x0c, 0x74, 0x6f, 0x5f, 0x70, 0x6f, 0x73, 0x74, 0x5f, 0x66,
+     0x6f, 0x72, 0x6b, 0x18, 0x12, 0x20, 0x01, 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, 0x53,
+     0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
+     0x2e, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x52, 0x0a, 0x74, 0x6f, 0x50, 0x6f,
+     0x73, 0x74, 0x46, 0x6f, 0x72, 0x6b, 0x12, 0x62, 0x0a, 0x17, 0x74, 0x6f,
+     0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x68,
+     0x72, 0x65, 0x61, 0x64, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x13, 0x20,
+     0x01, 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, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70,
+     0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x53, 0x6c, 0x69, 0x63, 0x65,
+     0x52, 0x14, 0x74, 0x6f, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79,
+     0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x4d, 0x61, 0x69, 0x6e, 0x12, 0x5b,
+     0x0a, 0x13, 0x74, 0x6f, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x61, 0x70,
+     0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x14, 0x20,
+     0x01, 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, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70,
+     0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x53, 0x6c, 0x69, 0x63, 0x65,
+     0x52, 0x11, 0x74, 0x6f, 0x42, 0x69, 0x6e, 0x64, 0x41, 0x70, 0x70, 0x6c,
+     0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x51, 0x0a, 0x0e, 0x74,
+     0x69, 0x6d, 0x65, 0x5f, 0x70, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x6f, 0x72,
+     0x6b, 0x18, 0x10, 0x20, 0x01, 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, 0x53, 0x74, 0x61,
+     0x72, 0x74, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x53,
+     0x6c, 0x69, 0x63, 0x65, 0x52, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x50, 0x6f,
+     0x73, 0x74, 0x46, 0x6f, 0x72, 0x6b, 0x4a, 0x04, 0x08, 0x0c, 0x10, 0x0d,
+     0x4a, 0x04, 0x08, 0x0d, 0x10, 0x0e, 0x4a, 0x04, 0x08, 0x0e, 0x10, 0x0f,
+     0x4a, 0x04, 0x08, 0x0f, 0x10, 0x10, 0x1a, 0x5c, 0x0a, 0x0a, 0x48, 0x73,
+     0x63, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x4e, 0x0a, 0x0c,
+     0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70,
+     0x18, 0x01, 0x20, 0x01, 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, 0x53, 0x74, 0x61, 0x72,
+     0x74, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x53, 0x6c,
+     0x69, 0x63, 0x65, 0x52, 0x0b, 0x66, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61,
+     0x72, 0x74, 0x75, 0x70, 0x1a, 0xc2, 0x03, 0x0a, 0x07, 0x53, 0x74, 0x61,
+     0x72, 0x74, 0x75, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72,
+     0x74, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
+     0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x49, 0x64, 0x12,
+     0x21, 0x0a, 0x0c, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6e,
+     0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70,
+     0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21,
+     0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6e, 0x61,
+     0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72,
+     0x6f, 0x63, 0x65, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2c, 0x0a,
+     0x12, 0x7a, 0x79, 0x67, 0x6f, 0x74, 0x65, 0x5f, 0x6e, 0x65, 0x77, 0x5f,
+     0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28,
+     0x08, 0x52, 0x10, 0x7a, 0x79, 0x67, 0x6f, 0x74, 0x65, 0x4e, 0x65, 0x77,
+     0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x43, 0x0a, 0x1e, 0x61,
+     0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x5f, 0x68, 0x6f, 0x73, 0x74,
+     0x69, 0x6e, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f,
+     0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52,
+     0x1b, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x48, 0x6f, 0x73,
+     0x74, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x43,
+     0x6f, 0x75, 0x6e, 0x74, 0x12, 0x58, 0x0a, 0x0e, 0x74, 0x6f, 0x5f, 0x66,
+     0x69, 0x72, 0x73, 0x74, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x05,
+     0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 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, 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, 0x12, 0x41,
+     0x0a, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x18, 0x07, 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, 0x42, 0x0a, 0x03, 0x68, 0x73, 0x63,
+     0x18, 0x08, 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, 0x53, 0x74, 0x61, 0x72,
+     0x74, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x48, 0x73,
+     0x63, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x03, 0x68, 0x73,
+     0x63, 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, 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, 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,
+     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, 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, 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, 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,
+     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,
-     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,
+     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, 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,
-     0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x1a, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f,
+     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, 0x98, 0x04, 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, 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, 0x1a, 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,
+     0x2f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6d, 0x65, 0x74,
+     0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,
+     0x8f, 0x03, 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, 0x4e, 0x0a,
+     0x05, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x69,
+     0x65, 0x6c, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
+     0x28, 0x09, 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4e, 0x61, 0x6d,
+     0x65, 0x12, 0x26, 0x0a, 0x0f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x74,
+     0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,
+     0x28, 0x09, 0x52, 0x0d, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x54, 0x79, 0x70,
+     0x65, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0xc9, 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, 0x40, 0x0a, 0x05, 0x66, 0x69, 0x65,
+     0x6c, 0x64, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 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, 0x46,
+     0x69, 0x65, 0x6c, 0x64, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4a,
+     0x04, 0x08, 0x03, 0x10, 0x04, 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, 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, 0xff, 0x04, 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, 0x6a, 0x61, 0x76,
+     0x61, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f,
+     0x67, 0x72, 0x61, 0x6d, 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,
+     0xf8, 0x03, 0x0a, 0x11, 0x4a, 0x61, 0x76, 0x61, 0x48, 0x65, 0x61, 0x70,
+     0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x12, 0x57, 0x0a,
+     0x0e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x73, 0x74,
+     0x61, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 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,
+     0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 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, 0x75, 0x0a, 0x09, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f,
+     0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f,
+     0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
+     0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09,
+     0x6f, 0x62, 0x6a, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20,
+     0x01, 0x28, 0x0d, 0x52, 0x08, 0x6f, 0x62, 0x6a, 0x43, 0x6f, 0x75, 0x6e,
+     0x74, 0x12, 0x2e, 0x0a, 0x13, 0x72, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62,
+     0x6c, 0x65, 0x5f, 0x6f, 0x62, 0x6a, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74,
+     0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x72, 0x65, 0x61, 0x63,
+     0x68, 0x61, 0x62, 0x6c, 0x65, 0x4f, 0x62, 0x6a, 0x43, 0x6f, 0x75, 0x6e,
+     0x74, 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, 0x4b, 0x0a, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x5f,
+     0x63, 0x6f, 0x75, 0x6e, 0x74, 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, 0x4a, 0x61, 0x76, 0x61, 0x48, 0x65,
+     0x61, 0x70, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x2e,
+     0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x09, 0x74,
+     0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0xab, 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, 0x43, 0x0a, 0x07, 0x73, 0x61, 0x6d,
+     0x70, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29,
+     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, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x2e, 0x53,
+     0x61, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x07, 0x73, 0x61, 0x6d, 0x70, 0x6c,
+     0x65, 0x73, 0x0a, 0xfa, 0x04, 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, 0xf7, 0x03, 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, 0xe6, 0x01, 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, 0x1b, 0x0a, 0x09, 0x6f, 0x62, 0x6a, 0x5f,
+     0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52,
+     0x08, 0x6f, 0x62, 0x6a, 0x43, 0x6f, 0x75, 0x6e, 0x74, 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, 0x12, 0x2e, 0x0a,
+     0x13, 0x72, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6f,
+     0x62, 0x6a, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01,
+     0x28, 0x03, 0x52, 0x11, 0x72, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c,
+     0x65, 0x4f, 0x62, 0x6a, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x32, 0x0a,
+     0x16, 0x61, 0x6e, 0x6f, 0x6e, 0x5f, 0x72, 0x73, 0x73, 0x5f, 0x61, 0x6e,
+     0x64, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18,
+     0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x61, 0x6e, 0x6f, 0x6e, 0x52,
+     0x73, 0x73, 0x41, 0x6e, 0x64, 0x53, 0x77, 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, 0xd0, 0x01, 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, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f,
+     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, 0x22, 0x85, 0x01, 0x0a, 0x15, 0x41,
+     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61,
+     0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x34, 0x0a, 0x16,
+     0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63,
+     0x61, 0x74, 0x65, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01,
+     0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x44,
+     0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x46, 0x72, 0x61, 0x6d,
+     0x65, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63,
+     0x61, 0x74, 0x65, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x5f, 0x6c,
+     0x6f, 0x67, 0x67, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52,
+     0x15, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x46, 0x72,
+     0x61, 0x6d, 0x65, 0x73, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x64, 0x0a, 0xbb,
+     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, 0x74,
+     0x61, 0x73, 0x6b, 0x5f, 0x6e, 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, 0xf5, 0x01, 0x0a,
+     0x10, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x54, 0x61, 0x73, 0x6b,
+     0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x07, 0x70, 0x72, 0x6f,
+     0x63, 0x65, 0x73, 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,
+     0x54, 0x61, 0x73, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x2e, 0x50, 0x72,
+     0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65,
+     0x73, 0x73, 0x1a, 0x9b, 0x01, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x63, 0x65,
+     0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x01, 0x20,
+     0x01, 0x28, 0x03, 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, 0x1f, 0x0a, 0x0b, 0x74,
+     0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03,
+     0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64,
+     0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18,
+     0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x28,
+     0x0a, 0x10, 0x75, 0x69, 0x64, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67,
+     0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09,
+     0x52, 0x0e, 0x75, 0x69, 0x64, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65,
+     0x4e, 0x61, 0x6d, 0x65, 0x0a, 0x81, 0x06, 0x0a, 0x41, 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, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f,
+     0x74, 0x69, 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74,
+     0x65, 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, 0xf2, 0x04, 0x0a, 0x1e, 0x41, 0x6e, 0x64, 0x72,
+     0x6f, 0x69, 0x64, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x54, 0x69, 0x6d,
+     0x65, 0x49, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x72,
+     0x69, 0x63, 0x12, 0x55, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73,
+     0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e,
+     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x54,
+     0x68, 0x72, 0x65, 0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x53,
+     0x74, 0x61, 0x74, 0x65, 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, 0x1a, 0x4f, 0x0a, 0x11, 0x4d, 0x65, 0x74,
+     0x72, 0x69, 0x63, 0x73, 0x42, 0x79, 0x43, 0x6f, 0x72, 0x65, 0x54, 0x79,
+     0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x74,
+     0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63,
+     0x6f, 0x72, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72,
+     0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x73, 0x18, 0x02, 0x20,
+     0x01, 0x28, 0x03, 0x52, 0x09, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65,
+     0x4d, 0x73, 0x1a, 0x90, 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, 0x72, 0x0a,
+     0x14, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x62, 0x79, 0x5f,
+     0x63, 0x6f, 0x72, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20,
+     0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e,
+     0x64, 0x72, 0x6f, 0x69, 0x64, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x54,
+     0x69, 0x6d, 0x65, 0x49, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x65,
+     0x74, 0x72, 0x69, 0x63, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+     0x42, 0x79, 0x43, 0x6f, 0x72, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x11,
+     0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x42, 0x79, 0x43, 0x6f, 0x72,
+     0x65, 0x54, 0x79, 0x70, 0x65, 0x1a, 0x94, 0x02, 0x0a, 0x07, 0x50, 0x72,
+     0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x43, 0x0a, 0x08, 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, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61,
+     0x74, 0x61, 0x12, 0x72, 0x0a, 0x14, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+     0x73, 0x5f, 0x62, 0x79, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x74, 0x79,
+     0x70, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x70,
+     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x54, 0x68,
+     0x72, 0x65, 0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x53, 0x74,
+     0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x4d, 0x65,
+     0x74, 0x72, 0x69, 0x63, 0x73, 0x42, 0x79, 0x43, 0x6f, 0x72, 0x65, 0x54,
+     0x79, 0x70, 0x65, 0x52, 0x11, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+     0x42, 0x79, 0x43, 0x6f, 0x72, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x50,
+     0x0a, 0x07, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 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, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x54,
+     0x69, 0x6d, 0x65, 0x49, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x65,
+     0x74, 0x72, 0x69, 0x63, 0x2e, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x52,
+     0x07, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x0a, 0xae, 0x19, 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, 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, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6c,
+     0x69, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 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, 0x1a, 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, 0x1a, 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, 0x6a, 0x61, 0x76, 0x61, 0x5f,
+     0x68, 0x65, 0x61, 0x70, 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72,
+     0x61, 0x6d, 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, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x68,
+     0x65, 0x61, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 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, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73,
-     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,
-     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, 0x2e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x65,
-     0x72, 0x72, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x73, 0x45, 0x6e, 0x74,
-     0x72, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f,
-     0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x73, 0x18,
-     0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x74, 0x72, 0x61, 0x63, 0x65,
-     0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x73, 0x12, 0x1d,
-     0x0a, 0x0a, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64,
-     0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x72, 0x61, 0x63,
-     0x65, 0x55, 0x75, 0x69, 0x64, 0x12, 0x3a, 0x0a, 0x19, 0x61, 0x6e, 0x64,
-     0x72, 0x6f, 0x69, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x66,
-     0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x04,
-     0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
-     0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x46, 0x69, 0x6e, 0x67, 0x65, 0x72,
-     0x70, 0x72, 0x69, 0x6e, 0x74, 0x12, 0x49, 0x0a, 0x21, 0x73, 0x74, 0x61,
-     0x74, 0x73, 0x64, 0x5f, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x69,
-     0x6e, 0x67, 0x5f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
-     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, 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,
+     0x2f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x65, 0x74,
+     0x72, 0x69, 0x63, 0x73, 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, 0x74, 0x61, 0x73, 0x6b,
+     0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x1a, 0x41, 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, 0x74, 0x68,
+     0x72, 0x65, 0x61, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x69, 0x6e,
+     0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+     0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc7, 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, 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,
+     0x2e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x65, 0x72, 0x72, 0x6f,
+     0x72, 0x53, 0x74, 0x61, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
+     0x2a, 0x0a, 0x11, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x64, 0x75, 0x72,
+     0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01,
+     0x28, 0x03, 0x52, 0x0f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x44, 0x75, 0x72,
+     0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74,
+     0x72, 0x61, 0x63, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20,
+     0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x72, 0x61, 0x63, 0x65, 0x55, 0x75,
+     0x69, 0x64, 0x12, 0x3a, 0x0a, 0x19, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+     0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x66, 0x69, 0x6e, 0x67,
+     0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28,
+     0x09, 0x52, 0x17, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x42, 0x75,
+     0x69, 0x6c, 0x64, 0x46, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69,
+     0x6e, 0x74, 0x12, 0x49, 0x0a, 0x21, 0x73, 0x74, 0x61, 0x74, 0x73, 0x64,
+     0x5f, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f,
+     0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 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, 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, 0x12,
+     0x23, 0x0a, 0x0d, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x72, 0x69,
+     0x67, 0x67, 0x65, 0x72, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c,
+     0x74, 0x72, 0x61, 0x63, 0x65, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72,
+     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, 0x95, 0x0d,
+     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, 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,
+     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, 0x52, 0x0a, 0x13, 0x6a, 0x61, 0x76,
+     0x61, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f,
+     0x67, 0x72, 0x61, 0x6d, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22,
+     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, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x52, 0x11,
+     0x6a, 0x61, 0x76, 0x61, 0x48, 0x65, 0x61, 0x70, 0x48, 0x69, 0x73, 0x74,
+     0x6f, 0x67, 0x72, 0x61, 0x6d, 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, 0x12, 0x4f, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61,
+     0x79, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x16, 0x20,
+     0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e,
+     0x64, 0x72, 0x6f, 0x69, 0x64, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79,
+     0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0e, 0x64, 0x69, 0x73,
+     0x70, 0x6c, 0x61, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12,
+     0x4f, 0x0a, 0x12, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x74,
+     0x61, 0x73, 0x6b, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x17, 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, 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,
+     0x64, 0x72, 0x6f, 0x69, 0x64, 0x54, 0x61, 0x73, 0x6b, 0x4e, 0x61, 0x6d,
+     0x65, 0x73, 0x52, 0x10, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x54,
+     0x61, 0x73, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x6f, 0x0a, 0x1c,
+     0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x74, 0x68, 0x72, 0x65,
+     0x61, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x5f, 0x73,
+     0x74, 0x61, 0x74, 0x65, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f,
      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}};
+     0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+     0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x49, 0x6e,
+     0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52,
+     0x18, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x54, 0x68, 0x72, 0x65,
+     0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x53, 0x74, 0x61, 0x74,
+     0x65, 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/metrics.h b/src/trace_processor/metrics/metrics.h
index 589413b..8568c15 100644
--- a/src/trace_processor/metrics/metrics.h
+++ b/src/trace_processor/metrics/metrics.h
@@ -27,7 +27,7 @@
 #include "perfetto/protozero/message.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
 #include "perfetto/trace_processor/trace_processor.h"
-#include "src/trace_processor/descriptors.h"
+#include "src/trace_processor/util/descriptors.h"
 
 #include "protos/perfetto/trace_processor/metrics_impl.pbzero.h"
 
diff --git a/src/trace_processor/metrics/trace_metadata.sql b/src/trace_processor/metrics/trace_metadata.sql
index c7e68e8..3891d68 100644
--- a/src/trace_processor/metrics/trace_metadata.sql
+++ b/src/trace_processor/metrics/trace_metadata.sql
@@ -38,5 +38,10 @@
  'trace_size_bytes', (
     SELECT int_value FROM metadata
     WHERE name = 'trace_size_bytes'
+  ),
+  'trace_trigger', (
+    SELECT RepeatedField(slice.name)
+    FROM track JOIN slice ON track.id = slice.track_id
+    WHERE track.name = 'Trace Triggers'
   )
 );
diff --git a/src/trace_processor/read_trace.cc b/src/trace_processor/read_trace.cc
index 0d73cf9..3a655c6 100644
--- a/src/trace_processor/read_trace.cc
+++ b/src/trace_processor/read_trace.cc
@@ -17,8 +17,14 @@
 #include "perfetto/trace_processor/read_trace.h"
 
 #include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/base/utils.h"
 #include "perfetto/trace_processor/trace_processor.h"
 
+#include "src/trace_processor/importers/gzip/gzip_utils.h"
+
+#include "protos/perfetto/trace/trace.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
     PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
 #define PERFETTO_HAS_AIO_H() 1
@@ -120,5 +126,42 @@
   return util::OkStatus();
 }
 
+util::Status DecompressTrace(const uint8_t* data,
+                             size_t size,
+                             std::vector<uint8_t>* output) {
+  if (!gzip::IsGzipSupported()) {
+    return util::ErrStatus(
+        "Cannot decompress trace in build where zlib is disabled");
+  }
+
+  protos::pbzero::Trace::Decoder decoder(data, size);
+  GzipDecompressor decompressor;
+  for (auto it = decoder.packet(); it; ++it) {
+    protos::pbzero::TracePacket::Decoder packet(*it);
+    if (!packet.has_compressed_packets()) {
+      it->SerializeAndAppendTo(output);
+      continue;
+    }
+
+    // Make sure that to reset the stream between the gzip streams.
+    auto bytes = packet.compressed_packets();
+    decompressor.Reset();
+    decompressor.SetInput(bytes.data, bytes.size);
+
+    using ResultCode = GzipDecompressor::ResultCode;
+    uint8_t out[4096];
+    for (auto ret = ResultCode::kOk; ret != ResultCode::kEof;) {
+      auto res = decompressor.Decompress(out, base::ArraySize(out));
+      ret = res.ret;
+      if (ret == ResultCode::kError || ret == ResultCode::kNoProgress ||
+          ret == ResultCode::kNeedsMoreInput) {
+        return util::ErrStatus("Failed while decompressing stream");
+      }
+      output->insert(output->end(), out, out + res.bytes_written);
+    }
+  }
+  return util::OkStatus();
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/read_trace_integrationtest.cc b/src/trace_processor/read_trace_integrationtest.cc
new file mode 100644
index 0000000..5070e35
--- /dev/null
+++ b/src/trace_processor/read_trace_integrationtest.cc
@@ -0,0 +1,63 @@
+/*
+ * 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 "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/base/utils.h"
+#include "perfetto/trace_processor/read_trace.h"
+
+#include "src/base/test/utils.h"
+#include "test/gtest_and_gmock.h"
+
+#include "protos/perfetto/trace/trace.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+TEST(ReadTraceIntegrationTest, CompressedTrace) {
+  base::ScopedFstream f(fopen(
+      base::GetTestDataPath(std::string("test/data/compressed.pb")).c_str(),
+      "rb"));
+  std::vector<uint8_t> raw_trace;
+  while (!feof(*f)) {
+    uint8_t buf[4096];
+    auto rsize =
+        fread(reinterpret_cast<char*>(buf), 1, base::ArraySize(buf), *f);
+    raw_trace.insert(raw_trace.end(), buf, buf + rsize);
+  }
+
+  std::vector<uint8_t> decompressed;
+  decompressed.reserve(raw_trace.size());
+
+  util::Status status = trace_processor::DecompressTrace(
+      raw_trace.data(), raw_trace.size(), &decompressed);
+  ASSERT_TRUE(status.ok());
+
+  protos::pbzero::Trace::Decoder decoder(decompressed.data(),
+                                         decompressed.size());
+  uint32_t packet_count = 0;
+  for (auto it = decoder.packet(); it; ++it) {
+    protos::pbzero::TracePacket::Decoder packet(*it);
+    ASSERT_FALSE(packet.has_compressed_packets());
+    ++packet_count;
+  }
+  ASSERT_EQ(packet_count, 2412u);
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/row_iterators.cc b/src/trace_processor/row_iterators.cc
deleted file mode 100644
index 98816c5..0000000
--- a/src/trace_processor/row_iterators.cc
+++ /dev/null
@@ -1,104 +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/row_iterators.h"
-
-#include <algorithm>
-
-#include "src/trace_processor/sqlite/sqlite_utils.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-namespace {
-
-template <typename Iterator>
-uint32_t FindNextOffset(Iterator begin, Iterator end, uint32_t offset) {
-  auto prev_it = begin + static_cast<ptrdiff_t>(offset);
-  auto current_it = std::find(prev_it, end, true);
-  return static_cast<uint32_t>(std::distance(begin, current_it));
-}
-
-uint32_t FindNextOffset(const std::vector<bool>& filter,
-                        uint32_t offset,
-                        bool desc) {
-  if (desc)
-    return FindNextOffset(filter.rbegin(), filter.rend(), offset);
-  return FindNextOffset(filter.begin(), filter.end(), offset);
-}
-
-}  // namespace
-
-RowIterator::~RowIterator() = default;
-
-RangeRowIterator::RangeRowIterator(uint32_t start_row,
-                                   uint32_t end_row,
-                                   bool desc)
-    : start_row_(start_row), end_row_(end_row), desc_(desc) {}
-
-RangeRowIterator::RangeRowIterator(uint32_t start_row,
-                                   bool desc,
-                                   std::vector<bool> row_filter)
-    : start_row_(start_row),
-      end_row_(start_row_ + static_cast<uint32_t>(row_filter.size())),
-      desc_(desc),
-      row_filter_(std::move(row_filter)) {
-  if (start_row_ < end_row_)
-    offset_ = FindNextOffset(row_filter_, offset_, desc_);
-}
-
-void RangeRowIterator::NextRow() {
-  PERFETTO_DCHECK(!IsEnd());
-  offset_++;
-
-  if (!row_filter_.empty())
-    offset_ = FindNextOffset(row_filter_, offset_, desc_);
-}
-
-bool RangeRowIterator::IsEnd() {
-  return offset_ >= end_row_ - start_row_;
-}
-
-uint32_t RangeRowIterator::Row() {
-  return desc_ ? end_row_ - offset_ - 1 : start_row_ + offset_;
-}
-
-uint32_t RangeRowIterator::RowCount() const {
-  if (row_filter_.empty()) {
-    return end_row_ - start_row_;
-  }
-  auto count = std::count(row_filter_.begin(), row_filter_.end(), true);
-  return static_cast<uint32_t>(count);
-}
-
-VectorRowIterator::VectorRowIterator(std::vector<uint32_t> row_indices)
-    : row_indices_(std::move(row_indices)) {}
-VectorRowIterator::~VectorRowIterator() = default;
-
-void VectorRowIterator::NextRow() {
-  offset_++;
-}
-
-bool VectorRowIterator::IsEnd() {
-  return offset_ >= row_indices_.size();
-}
-
-uint32_t VectorRowIterator::Row() {
-  return row_indices_[offset_];
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/row_iterators.h b/src/trace_processor/row_iterators.h
deleted file mode 100644
index 8050044..0000000
--- a/src/trace_processor/row_iterators.h
+++ /dev/null
@@ -1,79 +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_ROW_ITERATORS_H_
-#define SRC_TRACE_PROCESSOR_ROW_ITERATORS_H_
-
-#include <stdint.h>
-#include <vector>
-
-namespace perfetto {
-namespace trace_processor {
-
-// Implements a strategy of yielding indices into a storage system to fulfil
-// a query.
-class RowIterator {
- public:
-  virtual ~RowIterator();
-
-  virtual void NextRow() = 0;
-  virtual uint32_t Row() = 0;
-  virtual bool IsEnd() = 0;
-};
-
-// A row iterator which iterates through a range of indicies in either ascending
-// or descending order and optionally skips rows depending on a bitvector.
-class RangeRowIterator : public RowIterator {
- public:
-  RangeRowIterator(uint32_t start_row, uint32_t end_row, bool desc);
-  RangeRowIterator(uint32_t start_row, bool desc, std::vector<bool> row_filter);
-
-  void NextRow() override;
-  bool IsEnd() override;
-  uint32_t Row() override;
-
-  uint32_t RowCount() const;
-
- private:
-  uint32_t start_row_ = 0;
-  uint32_t end_row_ = 0;
-  bool desc_ = false;
-  std::vector<bool> row_filter_;
-
-  // In non-desc mode, this is an offset from start_row_ while in desc mode,
-  // this is an offset from end_row_.
-  uint32_t offset_ = 0;
-};
-
-// A row iterator which yields row indices from a provided vector.
-class VectorRowIterator : public RowIterator {
- public:
-  explicit VectorRowIterator(std::vector<uint32_t> row_indices);
-  ~VectorRowIterator() override;
-
-  void NextRow() override;
-  bool IsEnd() override;
-  uint32_t Row() override;
-
- private:
-  std::vector<uint32_t> row_indices_;
-  uint32_t offset_ = 0;
-};
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_ROW_ITERATORS_H_
diff --git a/src/trace_processor/rpc/BUILD.gn b/src/trace_processor/rpc/BUILD.gn
index 02ef12f..be17496 100644
--- a/src/trace_processor/rpc/BUILD.gn
+++ b/src/trace_processor/rpc/BUILD.gn
@@ -28,6 +28,7 @@
   deps = [
     "../../../gn:default_deps",
     "../../../include/perfetto/trace_processor",
+    "../../../protos/perfetto/metrics:zero",
     "../../../protos/perfetto/trace_processor:zero",
     "../../base",
     "../../protozero",
@@ -44,6 +45,7 @@
       ":rpc",
       "../../../gn:default_deps",
       "../../../include/perfetto/trace_processor",
+      "../../../protos/perfetto/metrics:zero",
       "../../../protos/perfetto/trace_processor:zero",
       "../../base",
       "../../base:unix_socket",
@@ -54,9 +56,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/rpc/httpd.cc b/src/trace_processor/rpc/httpd.cc
index 73c1bca..d56fd69 100644
--- a/src/trace_processor/rpc/httpd.cc
+++ b/src/trace_processor/rpc/httpd.cc
@@ -40,7 +40,6 @@
 namespace {
 
 constexpr char kBindAddr[] = "127.0.0.1:9001";
-constexpr auto kBlocking = base::UnixSocket::BlockingMode::kBlocking;
 
 // 32 MiB payload + 128K for HTTP headers.
 constexpr size_t kMaxRequestSize = (32 * 1024 + 128) * 1024;
@@ -111,9 +110,9 @@
   Append(response, "Content-Length: ");
   Append(response, std::to_string(body_len));
   Append(response, "\r\n\r\n");  // End-of-headers marker.
-  sock->Send(response.data(), response.size(), /*fd=*/-1, kBlocking);
+  sock->Send(response.data(), response.size());
   if (body_len)
-    sock->Send(body, body_len, /*fd=*/-1, kBlocking);
+    sock->Send(body, body_len);
 }
 
 void ShutdownBadRequest(base::UnixSocket* sock, const char* reason) {
@@ -298,6 +297,13 @@
                      buf.size());
   }
 
+  if (req.uri == "/compute_metric") {
+    std::vector<uint8_t> res = trace_processor_rpc_.ComputeMetric(
+        reinterpret_cast<const uint8_t*>(req.body.data()), req.body.size());
+    return HttpReply(client->sock.get(), "200 OK", headers, res.data(),
+                     res.size());
+  }
+
   return HttpReply(client->sock.get(), "404 Not Found", headers);
 }
 
diff --git a/src/trace_processor/rpc/rpc.cc b/src/trace_processor/rpc/rpc.cc
index e09d0ae..f98f2d1 100644
--- a/src/trace_processor/rpc/rpc.cc
+++ b/src/trace_processor/rpc/rpc.cc
@@ -21,6 +21,7 @@
 #include "perfetto/base/time.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
 #include "perfetto/trace_processor/trace_processor.h"
+#include "protos/perfetto/metrics/metrics.pbzero.h"
 #include "protos/perfetto/trace_processor/trace_processor.pbzero.h"
 
 namespace perfetto {
@@ -222,5 +223,30 @@
     trace_processor_->RestoreInitialTables();
 }
 
+std::vector<uint8_t> Rpc::ComputeMetric(const uint8_t* data, size_t len) {
+  protozero::HeapBuffered<protos::pbzero::ComputeMetricResult> result;
+  if (!trace_processor_) {
+    result->set_error("Null trace processor instance");
+    return result.SerializeAsArray();
+  }
+
+  protos::pbzero::ComputeMetricArgs::Decoder args(data, len);
+  std::vector<std::string> metric_names;
+  for (auto it = args.metric_names(); it; ++it) {
+    metric_names.emplace_back(it->as_std_string());
+  }
+
+  std::vector<uint8_t> metrics_proto;
+  util::Status status =
+      trace_processor_->ComputeMetric(metric_names, &metrics_proto);
+  if (status.ok()) {
+    auto* metrics = result->set_metrics();
+    metrics->AppendRawProtoBytes(metrics_proto.data(), metrics_proto.size());
+  } else {
+    result->set_error(status.message());
+  }
+  return result.SerializeAsArray();
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/rpc/rpc.h b/src/trace_processor/rpc/rpc.h
index 22a12fa..5896bc4 100644
--- a/src/trace_processor/rpc/rpc.h
+++ b/src/trace_processor/rpc/rpc.h
@@ -61,6 +61,7 @@
   std::vector<uint8_t> RawQuery(const uint8_t* args, size_t len);
   void RestoreInitialTables();
   std::string GetCurrentTraceName();
+  std::vector<uint8_t> ComputeMetric(const uint8_t* data, size_t len);
 
  private:
   void MaybePrintProgress();
diff --git a/src/trace_processor/rpc/wasm_bridge.cc b/src/trace_processor/rpc/wasm_bridge.cc
index 82ae660..041cbed 100644
--- a/src/trace_processor/rpc/wasm_bridge.cc
+++ b/src/trace_processor/rpc/wasm_bridge.cc
@@ -89,6 +89,14 @@
           static_cast<uint32_t>(res.size()));
 }
 
+void EMSCRIPTEN_KEEPALIVE trace_processor_compute_metric(uint32_t);
+void trace_processor_compute_metric(uint32_t size) {
+  std::vector<uint8_t> res =
+      g_trace_processor_rpc->ComputeMetric(g_req_buf, size);
+  g_reply(reinterpret_cast<const char*>(res.data()),
+          static_cast<uint32_t>(res.size()));
+}
+
 }  // extern "C"
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/sched_slice_table.cc b/src/trace_processor/sched_slice_table.cc
deleted file mode 100644
index 49d4a0a..0000000
--- a/src/trace_processor/sched_slice_table.cc
+++ /dev/null
@@ -1,210 +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/sched_slice_table.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-SchedSliceTable::SchedSliceTable(sqlite3*, const TraceStorage* storage)
-    : storage_(storage) {}
-
-void SchedSliceTable::RegisterTable(sqlite3* db, const TraceStorage* storage) {
-  SqliteTable::Register<SchedSliceTable>(db, storage, "sched");
-}
-
-StorageSchema SchedSliceTable::CreateStorageSchema() {
-  const auto& slices = storage_->slices();
-  return StorageSchema::Builder()
-      .AddOrderedNumericColumn("ts", &slices.start_ns())
-      .AddNumericColumn("cpu", &slices.cpus())
-      .AddNumericColumn("dur", &slices.durations())
-      .AddGenericNumericColumn(
-          "ts_end", TsEndAccessor(&slices.start_ns(), &slices.durations()))
-      .AddNumericColumn("utid", &slices.utids(), &slices.rows_for_utids())
-      .AddColumn<EndStateColumn>("end_state", &slices.end_state())
-      .AddNumericColumn("priority", &slices.priorities())
-      .AddGenericNumericColumn("row_id", RowAccessor())
-      .Build({"cpu", "ts"});
-}
-
-uint32_t SchedSliceTable::RowCount() {
-  return static_cast<uint32_t>(storage_->slices().slice_count());
-}
-
-int SchedSliceTable::BestIndex(const QueryConstraints& qc,
-                               BestIndexInfo* info) {
-  info->estimated_cost = EstimateQueryCost(qc);
-
-  // We should be able to handle any constraint and any order by clause given
-  // to us.
-  info->sqlite_omit_order_by = true;
-  auto& omit_cs = info->sqlite_omit_constraint;
-  std::fill(omit_cs.begin(), omit_cs.end(), true);
-
-  return SQLITE_OK;
-}
-
-uint32_t SchedSliceTable::EstimateQueryCost(const QueryConstraints& qc) {
-  const auto& cs = qc.constraints();
-
-  size_t ts_idx = schema().ColumnIndexFromName("ts");
-  auto has_ts_column = [ts_idx](const QueryConstraints::Constraint& c) {
-    return c.column == static_cast<int>(ts_idx);
-  };
-  bool has_time_constraint = std::any_of(cs.begin(), cs.end(), has_ts_column);
-  if (has_time_constraint) {
-    // If there is a constraint on ts, we can do queries very fast (O(log n))
-    // so always make this preferred if available.
-    return 10;
-  }
-
-  size_t utid_idx = schema().ColumnIndexFromName("utid");
-  auto has_utid_eq_cs = [utid_idx](const QueryConstraints::Constraint& c) {
-    return c.column == static_cast<int>(utid_idx) && sqlite_utils::IsOpEq(c.op);
-  };
-  bool has_utid_eq = std::any_of(cs.begin(), cs.end(), has_utid_eq_cs);
-  if (has_utid_eq) {
-    // The other column which is often joined on is utid. Sometimes, doing
-    // nested subqueries on the thread table is faster but with some queries,
-    // 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_table().row_count();
-  }
-
-  // If we get to this point, we do not have any special filter logic so
-  // simply return the number of rows.
-  return RowCount();
-}
-
-SchedSliceTable::EndStateColumn::EndStateColumn(
-    std::string col_name,
-    const std::deque<ftrace_utils::TaskState>* deque)
-    : StorageColumn(col_name, false), deque_(deque) {
-  for (uint16_t i = 0; i < state_strings_.size(); i++) {
-    state_strings_[i] = ftrace_utils::TaskState(i).ToString();
-  }
-}
-SchedSliceTable::EndStateColumn::~EndStateColumn() = default;
-
-void SchedSliceTable::EndStateColumn::ReportResult(sqlite3_context* ctx,
-                                                   uint32_t row) const {
-  const auto& state = (*deque_)[row];
-  if (state.is_valid()) {
-    PERFETTO_CHECK(state.raw_state() < state_strings_.size());
-    sqlite3_result_text(ctx, state_strings_[state.raw_state()].data(), -1,
-                        sqlite_utils::kSqliteStatic);
-  } else {
-    sqlite3_result_null(ctx);
-  }
-}
-
-void SchedSliceTable::EndStateColumn::Filter(int op,
-                                             sqlite3_value* value,
-                                             FilteredRowIndex* index) const {
-  switch (op) {
-    case SQLITE_INDEX_CONSTRAINT_ISNULL:
-    case SQLITE_INDEX_CONSTRAINT_ISNOTNULL: {
-      bool non_nulls = op == SQLITE_INDEX_CONSTRAINT_ISNOTNULL;
-      index->FilterRows([this, non_nulls](uint32_t row) {
-        const auto& state = (*deque_)[row];
-        return state.is_valid() == non_nulls;
-      });
-      break;
-    }
-    case SQLITE_INDEX_CONSTRAINT_EQ:
-    case SQLITE_INDEX_CONSTRAINT_NE:
-    case SQLITE_INDEX_CONSTRAINT_MATCH:
-      FilterOnState(op, value, index);
-      break;
-    default:
-      index->set_error("Unsupported op given to filter on end_state");
-      break;
-  }
-}
-
-void SchedSliceTable::EndStateColumn::FilterOnState(
-    int op,
-    sqlite3_value* value,
-    FilteredRowIndex* index) const {
-  if (sqlite3_value_type(value) != SQLITE_TEXT) {
-    index->set_error("end_state can only be filtered using strings");
-    return;
-  }
-
-  const char* str = reinterpret_cast<const char*>(sqlite3_value_text(value));
-  ftrace_utils::TaskState compare(str);
-  if (!compare.is_valid()) {
-    index->set_error("Invalid end_state string given to filter");
-    return;
-  }
-
-  uint16_t raw_state = compare.raw_state();
-  if (op == SQLITE_INDEX_CONSTRAINT_EQ) {
-    index->FilterRows([this, raw_state](uint32_t row) {
-      const auto& state = (*deque_)[row];
-      return state.is_valid() && state.raw_state() == raw_state;
-    });
-  } else if (op == SQLITE_INDEX_CONSTRAINT_NE) {
-    index->FilterRows([this, raw_state](uint32_t row) {
-      const auto& state = (*deque_)[row];
-      return state.is_valid() && state.raw_state() != raw_state;
-    });
-  } else if (op == SQLITE_INDEX_CONSTRAINT_MATCH) {
-    index->FilterRows([this, compare](uint32_t row) {
-      const auto& state = (*deque_)[row];
-      if (!state.is_valid())
-        return false;
-      return (state.raw_state() & compare.raw_state()) == compare.raw_state();
-    });
-  } else {
-    PERFETTO_FATAL("Should never reach this state");
-  }
-}
-
-StorageColumn::Comparator SchedSliceTable::EndStateColumn::Sort(
-    const QueryConstraints::OrderBy& ob) const {
-  if (ob.desc) {
-    return [this](uint32_t f, uint32_t s) {
-      const auto& a = (*deque_)[f];
-      const auto& b = (*deque_)[s];
-      if (!a.is_valid()) {
-        return !b.is_valid() ? 0 : 1;
-      } else if (!b.is_valid()) {
-        return -1;
-      }
-      return sqlite_utils::CompareValuesAsc(a.raw_state(), b.raw_state());
-    };
-  }
-  return [this](uint32_t f, uint32_t s) {
-    const auto& a = (*deque_)[f];
-    const auto& b = (*deque_)[s];
-    if (!a.is_valid()) {
-      return !b.is_valid() ? 0 : -1;
-    } else if (!b.is_valid()) {
-      return 1;
-    }
-    return sqlite_utils::CompareValuesAsc(a.raw_state(), b.raw_state());
-  };
-}
-
-SqlValue::Type SchedSliceTable::EndStateColumn::GetType() const {
-  return SqlValue::Type::kString;
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/sched_slice_table.h b/src/trace_processor/sched_slice_table.h
deleted file mode 100644
index bcac09a..0000000
--- a/src/trace_processor/sched_slice_table.h
+++ /dev/null
@@ -1,75 +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_SCHED_SLICE_TABLE_H_
-#define SRC_TRACE_PROCESSOR_SCHED_SLICE_TABLE_H_
-
-#include "src/trace_processor/ftrace_utils.h"
-#include "src/trace_processor/storage_table.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-// The implementation of the SQLite table containing slices of CPU time with the
-// metadata for those slices.
-class SchedSliceTable : public StorageTable {
- public:
-  SchedSliceTable(sqlite3*, const TraceStorage* storage);
-
-  static void RegisterTable(sqlite3* db, const TraceStorage* storage);
-
-  // StorageTable implementation.
-  StorageSchema CreateStorageSchema() override;
-  uint32_t RowCount() override;
-  int BestIndex(const QueryConstraints&, BestIndexInfo*) override;
-
- private:
-  uint32_t EstimateQueryCost(const QueryConstraints& cs);
-
-  class EndStateColumn : public StorageColumn {
-   public:
-    EndStateColumn(std::string col_name,
-                   const std::deque<ftrace_utils::TaskState>* deque);
-    ~EndStateColumn() override;
-
-    void ReportResult(sqlite3_context*, uint32_t row) const override;
-
-    void Filter(int op, sqlite3_value*, FilteredRowIndex*) const override;
-
-    Comparator Sort(const QueryConstraints::OrderBy&) const override;
-
-    SqlValue::Type GetType() const override;
-
-   private:
-    static constexpr uint16_t kNumStateStrings =
-        ftrace_utils::TaskState::kMaxState + 1;
-    std::array<ftrace_utils::TaskState::TaskStateStr, kNumStateStrings>
-        state_strings_;
-
-    void FilterOnState(int op,
-                       sqlite3_value* value,
-                       FilteredRowIndex* index) const;
-
-    const std::deque<ftrace_utils::TaskState>* deque_ = nullptr;
-  };
-
-  const TraceStorage* const storage_;
-};
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_SCHED_SLICE_TABLE_H_
diff --git a/src/trace_processor/sched_slice_table_unittest.cc b/src/trace_processor/sched_slice_table_unittest.cc
deleted file mode 100644
index 7e624d0..0000000
--- a/src/trace_processor/sched_slice_table_unittest.cc
+++ /dev/null
@@ -1,251 +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/sched_slice_table.h"
-
-#include "src/trace_processor/args_tracker.h"
-#include "src/trace_processor/event_tracker.h"
-#include "src/trace_processor/importers/ftrace/sched_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 {
-
-using ::testing::ElementsAre;
-using ::testing::IsEmpty;
-using Column = SchedSliceTable::Column;
-
-class SchedSliceTableTest : public ::testing::Test {
- public:
-  SchedSliceTableTest() {
-    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_.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_));
-    sched_tracker_ = SchedEventTracker::GetOrCreate(&context_);
-
-    SchedSliceTable::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);
-  }
-
- protected:
-  TraceProcessorContext context_;
-  ScopedDb db_;
-  ScopedStmt stmt_;
-  SchedEventTracker* sched_tracker_;
-};
-
-TEST_F(SchedSliceTableTest, RowsReturnedInCorrectOrderWithinCpu) {
-  uint32_t cpu = 3;
-  int64_t timestamp = 100;
-  uint32_t pid_1 = 2;
-  int64_t prev_state = 32;
-  static const char kCommProc1[] = "process1";
-  static const char kCommProc2[] = "process2";
-  uint32_t pid_2 = 4;
-  int32_t prio = 1024;
-  sched_tracker_->PushSchedSwitch(cpu, timestamp, pid_1, kCommProc2, prio,
-                                  prev_state, pid_2, kCommProc1, prio);
-  sched_tracker_->PushSchedSwitch(cpu, timestamp + 3, pid_2, kCommProc1, prio,
-                                  prev_state, pid_1, kCommProc2, prio);
-  sched_tracker_->PushSchedSwitch(cpu, timestamp + 4, pid_1, kCommProc2, prio,
-                                  prev_state, pid_2, kCommProc1, prio);
-  sched_tracker_->PushSchedSwitch(cpu, timestamp + 10, pid_2, kCommProc1, prio,
-                                  prev_state, pid_1, kCommProc2, prio);
-
-  PrepareValidStatement(
-      "SELECT dur, ts, cpu FROM sched where dur != 0 ORDER BY dur");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 0), 1 /* duration */);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 1), timestamp + 3);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 2), cpu);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 0), 3 /* duration */);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 1), timestamp);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 2), cpu);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 0), 6 /* duration */);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 1), timestamp + 4);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 2), cpu);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(SchedSliceTableTest, RowsReturnedInCorrectOrderBetweenCpu) {
-  uint32_t cpu_1 = 3;
-  uint32_t cpu_2 = 8;
-  uint32_t cpu_3 = 4;
-  int64_t timestamp = 100;
-  uint32_t pid_1 = 2;
-  int64_t prev_state = 32;
-  static const char kCommProc1[] = "process1";
-  static const char kCommProc2[] = "process2";
-  uint32_t pid_2 = 4;
-  int32_t prio = 1024;
-  sched_tracker_->PushSchedSwitch(cpu_3, timestamp - 2, pid_1, kCommProc2, prio,
-                                  prev_state, pid_2, kCommProc1, prio);
-  sched_tracker_->PushSchedSwitch(cpu_3, timestamp - 1, pid_2, kCommProc1, prio,
-                                  prev_state, pid_1, kCommProc2, prio);
-  sched_tracker_->PushSchedSwitch(cpu_1, timestamp, pid_1, kCommProc2, prio,
-                                  prev_state, pid_2, kCommProc1, prio);
-  sched_tracker_->PushSchedSwitch(cpu_2, timestamp + 3, pid_2, kCommProc1, prio,
-                                  prev_state, pid_1, kCommProc2, prio);
-  sched_tracker_->PushSchedSwitch(cpu_1, timestamp + 4, pid_2, kCommProc1, prio,
-                                  prev_state, pid_1, kCommProc2, prio);
-  sched_tracker_->PushSchedSwitch(cpu_2, timestamp + 10, pid_1, kCommProc2,
-                                  prio, prev_state, pid_2, kCommProc1, prio);
-
-  PrepareValidStatement(
-      "SELECT dur, ts, cpu FROM sched where dur != 0 ORDER BY dur desc");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 0), 7 /* duration */);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 1), timestamp + 3);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 2), cpu_2);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 0), 4 /* duration */);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 1), timestamp);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 2), cpu_1);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 0), 1 /* duration */);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 1), timestamp - 2);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 2), cpu_3);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(SchedSliceTableTest, FilterCpus) {
-  uint32_t cpu_1 = 3;
-  uint32_t cpu_2 = 8;
-  int64_t timestamp = 100;
-  uint32_t pid_1 = 2;
-  uint32_t prev_state = 32;
-  static const char kCommProc1[] = "process1";
-  static const char kCommProc2[] = "process2";
-  uint32_t pid_2 = 4;
-  int32_t prio = 1024;
-  sched_tracker_->PushSchedSwitch(cpu_1, timestamp, pid_1, kCommProc2, prio,
-                                  prev_state, pid_2, kCommProc1, prio);
-  sched_tracker_->PushSchedSwitch(cpu_2, timestamp + 3, pid_2, kCommProc1, prio,
-                                  prev_state, pid_1, kCommProc2, prio);
-  sched_tracker_->PushSchedSwitch(cpu_1, timestamp + 4, pid_2, kCommProc1, prio,
-                                  prev_state, pid_1, kCommProc2, prio);
-  sched_tracker_->PushSchedSwitch(cpu_2, timestamp + 10, pid_1, kCommProc2,
-                                  prio, prev_state, pid_2, kCommProc1, prio);
-
-  PrepareValidStatement(
-      "SELECT dur, ts, cpu FROM sched WHERE dur != 0 and cpu = 3");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 0), 4 /* duration */);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 1), timestamp);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 2), cpu_1);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(SchedSliceTableTest, UtidTest) {
-  uint32_t cpu = 3;
-  int64_t timestamp = 100;
-  uint32_t pid_1 = 2;
-  uint32_t prev_state = 32;
-  static const char kCommProc1[] = "process1";
-  static const char kCommProc2[] = "process2";
-  uint32_t pid_2 = 4;
-  int32_t prio = 1024;
-  sched_tracker_->PushSchedSwitch(cpu, timestamp, pid_1, kCommProc2, prio,
-                                  prev_state, pid_2, kCommProc1, prio);
-  sched_tracker_->PushSchedSwitch(cpu, timestamp + 3, pid_2, kCommProc1, prio,
-                                  prev_state, pid_1, kCommProc2, prio);
-  sched_tracker_->PushSchedSwitch(cpu, timestamp + 4, pid_1, kCommProc2, prio,
-                                  prev_state, pid_2, kCommProc1, prio);
-  sched_tracker_->PushSchedSwitch(cpu, timestamp + 10, pid_2, kCommProc1, prio,
-                                  prev_state, pid_1, kCommProc2, prio);
-
-  PrepareValidStatement("SELECT utid FROM sched where dur != 0 ORDER BY utid");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 0), 1 /* duration */);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 0), 1 /* duration */);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 0), 2 /* duration */);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(SchedSliceTableTest, TimestampFiltering) {
-  uint32_t cpu_5 = 5;
-  uint32_t cpu_7 = 7;
-  uint32_t pid_1 = 1;
-  uint32_t pid_2 = 2;
-  int64_t prev_state = 32;
-  int32_t prio = 1024;
-
-  // Fill |cpu_5| and |cpu_7) with one sched switch per time unit starting,
-  // respectively, @ T=50 and T=70.
-  for (int64_t i = 0; i <= 11; i++) {
-    sched_tracker_->PushSchedSwitch(cpu_5, 50 + i, pid_1, "pid_1", prio,
-                                    prev_state, pid_1, "pid_1", prio);
-  }
-  for (int64_t i = 0; i <= 11; i++) {
-    sched_tracker_->PushSchedSwitch(cpu_7, 70 + i, pid_2, "pid_2", prio,
-                                    prev_state, pid_2, "pid_2", prio);
-  }
-
-  auto query = [this](const std::string& where_clauses) {
-    PrepareValidStatement("SELECT ts from sched WHERE dur != 0 and " +
-                          where_clauses);
-    std::vector<int> res;
-    while (sqlite3_step(*stmt_) == SQLITE_ROW) {
-      res.push_back(sqlite3_column_int(*stmt_, 0));
-    }
-    return res;
-  };
-
-  ASSERT_THAT(query("ts > 55 and ts <= 60"), ElementsAre(56, 57, 58, 59, 60));
-  ASSERT_THAT(query("ts >= 55 and ts < 52"), IsEmpty());
-  ASSERT_THAT(query("ts >= 70 and ts < 71"), ElementsAre(70));
-  ASSERT_THAT(query("ts >= 59 and ts < 73"), ElementsAre(59, 60, 70, 71, 72));
-}
-
-}  // namespace
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/sqlite/BUILD.gn b/src/trace_processor/sqlite/BUILD.gn
index 418e517..1679172 100644
--- a/src/trace_processor/sqlite/BUILD.gn
+++ b/src/trace_processor/sqlite/BUILD.gn
@@ -19,22 +19,35 @@
     sources = [
       "db_sqlite_table.cc",
       "db_sqlite_table.h",
+      "query_cache.h",
       "query_constraints.cc",
       "query_constraints.h",
       "scoped_db.h",
+      "span_join_operator_table.cc",
+      "span_join_operator_table.h",
+      "sql_stats_table.cc",
+      "sql_stats_table.h",
       "sqlite3_str_split.cc",
       "sqlite3_str_split.h",
+      "sqlite_raw_table.cc",
+      "sqlite_raw_table.h",
       "sqlite_table.cc",
       "sqlite_table.h",
       "sqlite_utils.h",
+      "stats_table.cc",
+      "stats_table.h",
+      "window_operator_table.cc",
+      "window_operator_table.h",
     ]
     deps = [
+      "..:ftrace_descriptors",
       "../../../gn:default_deps",
       "../../../gn:sqlite",
       "../../../include/perfetto/trace_processor",
       "../../../protos/perfetto/trace/ftrace:zero",
       "../../base",
       "../db:lib",
+      "../storage",
       "../types",
     ]
   }
@@ -44,6 +57,7 @@
     sources = [
       "db_sqlite_table_unittest.cc",
       "query_constraints_unittest.cc",
+      "span_join_operator_table_unittest.cc",
       "sqlite3_str_split_unittest.cc",
     ]
     deps = [
diff --git a/src/trace_processor/sqlite/db_sqlite_table.cc b/src/trace_processor/sqlite/db_sqlite_table.cc
index 83789ea..e934d26 100644
--- a/src/trace_processor/sqlite/db_sqlite_table.cc
+++ b/src/trace_processor/sqlite/db_sqlite_table.cc
@@ -16,6 +16,7 @@
 
 #include "src/trace_processor/sqlite/db_sqlite_table.h"
 
+#include "src/trace_processor/sqlite/query_cache.h"
 #include "src/trace_processor/sqlite/sqlite_utils.h"
 
 namespace perfetto {
@@ -23,7 +24,7 @@
 
 namespace {
 
-FilterOp SqliteOpToFilterOp(int sqlite_op) {
+base::Optional<FilterOp> SqliteOpToFilterOp(int sqlite_op) {
   switch (sqlite_op) {
     case SQLITE_INDEX_CONSTRAINT_EQ:
     case SQLITE_INDEX_CONSTRAINT_IS:
@@ -44,9 +45,8 @@
     case SQLITE_INDEX_CONSTRAINT_ISNOTNULL:
       return FilterOp::kIsNotNull;
     case SQLITE_INDEX_CONSTRAINT_LIKE:
-      return FilterOp::kLike;
     case SQLITE_INDEX_CONSTRAINT_GLOB:
-      return FilterOp::kGlob;
+      return base::nullopt;
     default:
       PERFETTO_FATAL("Currently unsupported constraint");
   }
@@ -83,32 +83,60 @@
 
 }  // namespace
 
-DbSqliteTable::DbSqliteTable(sqlite3*, const Table* table) : table_(table) {}
+DbSqliteTable::DbSqliteTable(sqlite3*, Context context)
+    : cache_(context.cache),
+      schema_(std::move(context.schema)),
+      computation_(context.computation),
+      static_table_(context.static_table),
+      generator_(std::move(context.generator)) {}
 DbSqliteTable::~DbSqliteTable() = default;
 
 void DbSqliteTable::RegisterTable(sqlite3* db,
+                                  QueryCache* cache,
+                                  Table::Schema schema,
                                   const Table* table,
                                   const std::string& name) {
-  SqliteTable::Register<DbSqliteTable, const Table*>(db, table, name);
+  Context context{cache, schema, TableComputation::kStatic, table, nullptr};
+  SqliteTable::Register<DbSqliteTable, Context>(db, std::move(context), name);
+}
+
+void DbSqliteTable::RegisterTable(
+    sqlite3* db,
+    QueryCache* cache,
+    std::unique_ptr<DynamicTableGenerator> generator) {
+  Table::Schema schema = generator->CreateSchema();
+  std::string name = generator->TableName();
+
+  // Figure out if the table needs explicit args (in the form of constraints
+  // on hidden columns) passed to it in order to make the query valid.
+  util::Status status = generator->ValidateConstraints({});
+  bool requires_args = !status.ok();
+
+  Context context{cache, std::move(schema), TableComputation::kDynamic, nullptr,
+                  std::move(generator)};
+  SqliteTable::Register<DbSqliteTable, Context>(db, std::move(context), name,
+                                                false, requires_args);
 }
 
 util::Status DbSqliteTable::Init(int, const char* const*, Schema* schema) {
-  *schema = ComputeSchema(*table_, name().c_str());
+  *schema = ComputeSchema(schema_, name().c_str());
   return util::OkStatus();
 }
 
-SqliteTable::Schema DbSqliteTable::ComputeSchema(const Table& table,
+SqliteTable::Schema DbSqliteTable::ComputeSchema(const Table::Schema& schema,
                                                  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);
-    schema_cols.emplace_back(i, col.name(), col.type());
+  for (uint32_t i = 0; i < schema.columns.size(); ++i) {
+    const auto& col = schema.columns[i];
+    schema_cols.emplace_back(i, col.name, col.type, col.is_hidden);
   }
 
   // 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");
-  if (!col) {
+  auto it = std::find_if(
+      schema.columns.begin(), schema.columns.end(),
+      [](const Table::Schema::Column& c) { return c.name == "id"; });
+  if (it == schema.columns.end()) {
     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.",
@@ -116,38 +144,72 @@
   }
 
   std::vector<size_t> primary_keys;
-  primary_keys.emplace_back(col->index_in_table());
+  primary_keys.emplace_back(std::distance(schema.columns.begin(), it));
   return Schema(std::move(schema_cols), std::move(primary_keys));
 }
 
 int DbSqliteTable::BestIndex(const QueryConstraints& qc, BestIndexInfo* info) {
-  // TODO(lalitm): investigate SQLITE_INDEX_SCAN_UNIQUE for id columns.
-  auto cost_and_rows = EstimateCost(*table_, qc);
-  info->estimated_cost = cost_and_rows.cost;
-  info->estimated_rows = cost_and_rows.rows;
+  switch (computation_) {
+    case TableComputation::kStatic:
+      BestIndex(schema_, static_table_->row_count(), qc, info);
+      break;
+    case TableComputation::kDynamic:
+      util::Status status = generator_->ValidateConstraints(qc);
+      if (!status.ok())
+        return SQLITE_CONSTRAINT;
+      BestIndex(schema_, generator_->EstimateRowCount(), qc, info);
+      break;
+  }
   return SQLITE_OK;
 }
 
+void DbSqliteTable::BestIndex(const Table::Schema& schema,
+                              uint32_t row_count,
+                              const QueryConstraints& qc,
+                              BestIndexInfo* info) {
+  auto cost_and_rows = EstimateCost(schema, row_count, qc);
+  info->estimated_cost = cost_and_rows.cost;
+  info->estimated_rows = cost_and_rows.rows;
+
+  const auto& cs = qc.constraints();
+  for (uint32_t i = 0; i < cs.size(); ++i) {
+    // SqliteOpToFilterOp will return nullopt for any constraint which we don't
+    // support filtering ourselves. Only omit filtering by SQLite when we can
+    // handle filtering.
+    base::Optional<FilterOp> opt_op = SqliteOpToFilterOp(cs[i].op);
+    info->sqlite_omit_constraint[i] = opt_op.has_value();
+  }
+
+  // We can sort on any column correctly.
+  info->sqlite_omit_order_by = true;
+}
+
 int DbSqliteTable::ModifyConstraints(QueryConstraints* qc) {
+  ModifyConstraints(schema_, qc);
+  return SQLITE_OK;
+}
+
+void DbSqliteTable::ModifyConstraints(const Table::Schema& schema,
+                                      QueryConstraints* qc) {
   using C = QueryConstraints::Constraint;
 
   // Reorder constraints to consider the constraints on columns which are
   // cheaper to filter first.
   auto* cs = qc->mutable_constraints();
-  std::sort(cs->begin(), cs->end(), [this](const C& a, const C& b) {
+  std::sort(cs->begin(), cs->end(), [&schema](const C& a, const C& b) {
     uint32_t a_idx = static_cast<uint32_t>(a.column);
     uint32_t b_idx = static_cast<uint32_t>(b.column);
-    const auto& a_col = table_->GetColumn(a_idx);
-    const auto& b_col = table_->GetColumn(b_idx);
+    const auto& a_col = schema.columns[a_idx];
+    const auto& b_col = schema.columns[b_idx];
 
     // Id columns are always very cheap to filter on so try and get them
     // first.
-    if (a_col.IsId() && !b_col.IsId())
+    if (a_col.is_id && !b_col.is_id)
       return true;
 
     // Sorted columns are also quite cheap to filter so order them after
     // any id columns.
-    if (a_col.IsSorted() && !b_col.IsSorted())
+    if (a_col.is_sorted && !b_col.is_sorted)
       return true;
 
     // TODO(lalitm): introduce more orderings here based on empirical data.
@@ -171,20 +233,19 @@
   // constraints until the first non-sorted column or the first order by in
   // descending order.
   {
-    auto p = [this](const QueryConstraints::OrderBy& o) {
-      const auto& col = table_->GetColumn(static_cast<uint32_t>(o.iColumn));
-      return o.desc || !col.IsSorted();
+    auto p = [&schema](const QueryConstraints::OrderBy& o) {
+      const auto& col = schema.columns[static_cast<uint32_t>(o.iColumn)];
+      return o.desc || !col.is_sorted;
     };
     auto first_non_sorted_it = std::find_if(ob->rbegin(), ob->rend(), p);
     auto pop_count = std::distance(ob->rbegin(), first_non_sorted_it);
     ob->resize(ob->size() - static_cast<uint32_t>(pop_count));
   }
-
-  return SQLITE_OK;
 }
 
 DbSqliteTable::QueryCost DbSqliteTable::EstimateCost(
-    const Table& table,
+    const Table::Schema& schema,
+    uint32_t row_count,
     const QueryConstraints& qc) {
   // Currently our cost estimation algorithm is quite simplistic but is good
   // enough for the simplest cases.
@@ -199,7 +260,7 @@
   // end of filtering. Note that |current_row_count| should always be at least 1
   // unless we are absolutely certain that we will return no rows as otherwise
   // SQLite can make some bad choices.
-  uint32_t current_row_count = table.row_count();
+  uint32_t current_row_count = row_count;
 
   // If the table is empty, any constraint set only pays the fixed cost. Also we
   // can return 0 as the row count as we are certain that we will return no
@@ -213,8 +274,8 @@
   for (const auto& c : cs) {
     if (current_row_count < 2)
       break;
-    const auto& col = table.GetColumn(static_cast<uint32_t>(c.column));
-    if (sqlite_utils::IsOpEq(c.op) && col.IsId()) {
+    const auto& col_schema = schema.columns[static_cast<uint32_t>(c.column)];
+    if (sqlite_utils::IsOpEq(c.op) && col_schema.is_id) {
       // If we have an id equality constraint, it's a bit expensive to find
       // the exact row but it filters down to a single row.
       filter_cost += 100;
@@ -227,7 +288,7 @@
       // 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()
+      filter_cost += cs.size() == 1 || col_schema.is_sorted
                          ? (2 * current_row_count) / log2(current_row_count)
                          : current_row_count;
 
@@ -260,29 +321,41 @@
 }
 
 std::unique_ptr<SqliteTable::Cursor> DbSqliteTable::CreateCursor() {
-  return std::unique_ptr<Cursor>(new Cursor(this, table_));
+  return std::unique_ptr<Cursor>(new Cursor(this, cache_));
 }
 
-DbSqliteTable::Cursor::Cursor(SqliteTable* sqlite_table, const Table* table)
-    : SqliteTable::Cursor(sqlite_table), initial_db_table_(table) {}
+DbSqliteTable::Cursor::Cursor(DbSqliteTable* sqlite_table, QueryCache* cache)
+    : SqliteTable::Cursor(sqlite_table),
+      db_sqlite_table_(sqlite_table),
+      cache_(cache) {}
 
 void DbSqliteTable::Cursor::TryCacheCreateSortedTable(
     const QueryConstraints& qc,
     FilterHistory history) {
+  // Check if we have a cache. Some subclasses (e.g. the flamegraph table) may
+  // pass nullptr to disable caching.
+  if (!cache_)
+    return;
+
   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;
+
+    // Check if the new constraint set is cached by another cursor.
+    sorted_cache_table_ =
+        cache_->GetIfCached(upstream_table_, qc.constraints());
     return;
   }
 
   PERFETTO_DCHECK(history == FilterHistory::kSame);
 
+  // TODO(lalitm): all of the caching policy below should live in QueryCache and
+  // not here. This is only here temporarily to allow migration of sched without
+  // regressing UI performance and should be removed ASAP.
+
   // 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)
+  if (sorted_cache_table_ || repeated_cache_count_++ != kRepeatedThreshold)
     return;
 
   // If we have more than one constraint, we can't cache the table using
@@ -298,11 +371,14 @@
 
   // 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())
+  if (upstream_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}});
+  // Try again to get the result or start caching it.
+  sorted_cache_table_ =
+      cache_->GetOrCache(upstream_table_, qc.constraints(), [this, col]() {
+        return upstream_table_->Sort({Order{col, false}});
+      });
 }
 
 int DbSqliteTable::Cursor::Filter(const QueryConstraints& qc,
@@ -312,21 +388,23 @@
   // before the table's destructor.
   iterator_ = base::nullopt;
 
-  // 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());
+  uint32_t constraints_pos = 0;
   for (size_t i = 0; i < qc.constraints().size(); ++i) {
     const auto& cs = qc.constraints()[i];
     uint32_t col = static_cast<uint32_t>(cs.column);
 
-    FilterOp op = SqliteOpToFilterOp(cs.op);
-    SqlValue value = SqliteValueToSqlValue(argv[i]);
+    // If we get a nullopt FilterOp, that means we should allow SQLite
+    // to handle the constraint.
+    base::Optional<FilterOp> opt_op = SqliteOpToFilterOp(cs.op);
+    if (!opt_op)
+      continue;
 
-    constraints_[i] = Constraint{col, op, value};
+    SqlValue value = SqliteValueToSqlValue(argv[i]);
+    constraints_[constraints_pos++] = Constraint{col, *opt_op, value};
   }
+  constraints_.resize(constraints_pos);
 
   // We reuse this vector to reduce memory allocations on nested subqueries.
   orders_.resize(qc.order_by().size());
@@ -336,9 +414,36 @@
     orders_[i] = Order{col, static_cast<bool>(ob.desc)};
   }
 
+  // Setup the upstream table based on the computation state.
+  switch (db_sqlite_table_->computation_) {
+    case TableComputation::kStatic:
+      // If we have a static table, just set the upstream table to be the static
+      // table.
+      upstream_table_ = db_sqlite_table_->static_table_;
+
+      // Tries to create a sorted cached table which can be used to speed up
+      // filters below.
+      TryCacheCreateSortedTable(qc, history);
+      break;
+    case TableComputation::kDynamic:
+      // If we have a dynamically created table, regenerate the table based on
+      // the new constraints.
+      dynamic_table_ =
+          db_sqlite_table_->generator_->ComputeTable(constraints_, orders_);
+      upstream_table_ = dynamic_table_.get();
+      if (!upstream_table_)
+        return SQLITE_CONSTRAINT;
+      break;
+  }
+
   // Attempt to filter into a RowMap first - we'll figure out whether to apply
-  // this to the table or we should use the RowMap directly.
-  RowMap filter_map = SourceTable()->FilterToRowMap(constraints_);
+  // this to the table or we should use the RowMap directly. Also, if we are
+  // going to sort on the RowMap, it makes sense that we optimize for lookup
+  // speed so our sorting is not super slow.
+  RowMap::OptimizeFor optimize_for = orders_.empty()
+                                         ? RowMap::OptimizeFor::kMemory
+                                         : RowMap::OptimizeFor::kLookupSpeed;
+  RowMap filter_map = SourceTable()->FilterToRowMap(constraints_, optimize_for);
 
   // If we have no order by constraints and it's cheap for us to use the
   // RowMap, just use the RowMap directoy.
@@ -423,5 +528,7 @@
   return SQLITE_OK;
 }
 
+DbSqliteTable::DynamicTableGenerator::~DynamicTableGenerator() = default;
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/sqlite/db_sqlite_table.h b/src/trace_processor/sqlite/db_sqlite_table.h
index 2a606ec..64231de 100644
--- a/src/trace_processor/sqlite/db_sqlite_table.h
+++ b/src/trace_processor/sqlite/db_sqlite_table.h
@@ -18,6 +18,7 @@
 #define SRC_TRACE_PROCESSOR_SQLITE_DB_SQLITE_TABLE_H_
 
 #include "src/trace_processor/db/table.h"
+#include "src/trace_processor/sqlite/query_cache.h"
 #include "src/trace_processor/sqlite/sqlite_table.h"
 
 namespace perfetto {
@@ -26,9 +27,50 @@
 // Implements the SQLite table interface for db tables.
 class DbSqliteTable : public SqliteTable {
  public:
+  enum class TableComputation {
+    // Mode when the table is static (i.e. passed in at construction
+    // time).
+    kStatic,
+
+    // Mode when table is dynamically computed at filter time.
+    kDynamic,
+  };
+
+  // Interface which can be subclassed to allow generation of tables dynamically
+  // at filter time.
+  // This class is used to implement table-valued functions and other similar
+  // tables.
+  class DynamicTableGenerator {
+   public:
+    virtual ~DynamicTableGenerator();
+
+    // Returns the schema of the table that will be returned by ComputeTable.
+    virtual Table::Schema CreateSchema() = 0;
+
+    // Returns the name of the dynamic table.
+    // This will be used to register the table with SQLite.
+    virtual std::string TableName() = 0;
+
+    // Returns the estimated number of rows the table would generate.
+    virtual uint32_t EstimateRowCount() = 0;
+
+    // Checks that the constraint set is valid.
+    //
+    // Returning util::OkStatus means that the required constraints are present
+    // in |qc| for dynamically computing the table (e.g. any required
+    // constraints on hidden columns for table-valued functions are present).
+    virtual util::Status ValidateConstraints(const QueryConstraints& qc) = 0;
+
+    // Dynamically computes the table given the constraints and order by
+    // vectors.
+    virtual std::unique_ptr<Table> ComputeTable(
+        const std::vector<Constraint>& cs,
+        const std::vector<Order>& ob) = 0;
+  };
+
   class Cursor : public SqliteTable::Cursor {
    public:
-    Cursor(SqliteTable*, const Table* table);
+    Cursor(DbSqliteTable*, QueryCache*);
 
     Cursor(Cursor&&) noexcept = default;
     Cursor& operator=(Cursor&&) = default;
@@ -41,12 +83,6 @@
     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,
@@ -60,13 +96,20 @@
     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.
-      return sorted_cache_table_ ? &*sorted_cache_table_ : &*initial_db_table_;
+      return sorted_cache_table_ ? &*sorted_cache_table_ : upstream_table_;
     }
 
     Cursor(const Cursor&) = delete;
     Cursor& operator=(const Cursor&) = delete;
 
-    const Table* initial_db_table_ = nullptr;
+    DbSqliteTable* db_sqlite_table_ = nullptr;
+    QueryCache* cache_ = nullptr;
+
+    const Table* upstream_table_ = nullptr;
+
+    // Only valid for |db_sqlite_table_->computation_| ==
+    // TableComputation::kDynamic.
+    std::unique_ptr<Table> dynamic_table_;
 
     // Only valid for Mode::kSingleRow.
     base::Optional<uint32_t> single_row_;
@@ -80,7 +123,7 @@
     // Stores a sorted version of |db_table_| sorted on a repeated equals
     // constraint. This allows speeding up repeated subqueries in joins
     // significantly.
-    base::Optional<Table> sorted_cache_table_;
+    std::shared_ptr<Table> sorted_cache_table_;
 
     // Stores the count of repeated equality queries to decide whether it is
     // wortwhile to sort |db_table_| to create |sorted_cache_table_|.
@@ -95,12 +138,29 @@
     double cost;
     uint32_t rows;
   };
+  struct Context {
+    QueryCache* cache;
+    Table::Schema schema;
+    TableComputation computation;
+
+    // Only valid when computation == TableComputation::kStatic.
+    const Table* static_table;
+
+    // Only valid when computation == TableComputation::kDynamic.
+    std::unique_ptr<DynamicTableGenerator> generator;
+  };
 
   static void RegisterTable(sqlite3* db,
+                            QueryCache* cache,
+                            Table::Schema schema,
                             const Table* table,
                             const std::string& name);
 
-  DbSqliteTable(sqlite3*, const Table* table);
+  static void RegisterTable(sqlite3* db,
+                            QueryCache* cache,
+                            std::unique_ptr<DynamicTableGenerator> generator);
+
+  DbSqliteTable(sqlite3*, Context context);
   virtual ~DbSqliteTable() override;
 
   // Table implementation.
@@ -108,17 +168,35 @@
                     const char* const*,
                     SqliteTable::Schema*) override final;
   std::unique_ptr<SqliteTable::Cursor> CreateCursor() override;
-  int ModifyConstraints(QueryConstraints*) override;
-  int BestIndex(const QueryConstraints&, BestIndexInfo*) override;
+  int ModifyConstraints(QueryConstraints*) override final;
+  int BestIndex(const QueryConstraints&, BestIndexInfo*) override final;
 
-  static SqliteTable::Schema ComputeSchema(const Table& table,
+  // These static functions are useful to allow other callers to make use
+  // of them.
+  static SqliteTable::Schema ComputeSchema(const Table::Schema&,
                                            const char* table_name);
+  static void ModifyConstraints(const Table::Schema&, QueryConstraints*);
+  static void BestIndex(const Table::Schema&,
+                        uint32_t row_count,
+                        const QueryConstraints&,
+                        BestIndexInfo*);
 
   // static for testing.
-  static QueryCost EstimateCost(const Table& table, const QueryConstraints& qc);
+  static QueryCost EstimateCost(const Table::Schema&,
+                                uint32_t row_count,
+                                const QueryConstraints& qc);
 
  private:
-  const Table* table_ = nullptr;
+  QueryCache* cache_ = nullptr;
+  Table::Schema schema_;
+
+  TableComputation computation_ = TableComputation::kStatic;
+
+  // Only valid when computation_ == TableComputation::kStatic.
+  const Table* static_table_ = nullptr;
+
+  // Only valid when computation_ == TableComputation::kDynamic.
+  std::unique_ptr<DynamicTableGenerator> generator_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/sqlite/db_sqlite_table_unittest.cc b/src/trace_processor/sqlite/db_sqlite_table_unittest.cc
index 2fbc3e4..78b6961 100644
--- a/src/trace_processor/sqlite/db_sqlite_table_unittest.cc
+++ b/src/trace_processor/sqlite/db_sqlite_table_unittest.cc
@@ -1,3 +1,4 @@
+
 /*
  * Copyright (C) 2019 The Android Open Source Project
  *
@@ -22,78 +23,71 @@
 namespace trace_processor {
 namespace {
 
-class TestTable : public Table {
- public:
-  TestTable(uint32_t row_count) : Table(&pool_, nullptr) {
-    row_maps_.emplace_back(RowMap(0, row_count));
-    row_count_ = row_count;
-
-    columns_.emplace_back(Column::IdColumn(this, 0u, 0u));
-    columns_.emplace_back(
-        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_;
-};
+Table::Schema CreateSchema() {
+  Table::Schema schema;
+  schema.columns.push_back({"id", SqlValue::Type::kLong, true /* is_id */,
+                            true /* is_sorted */, false /* is_hidden */});
+  schema.columns.push_back({"type", SqlValue::Type::kLong, false /* is_id */,
+                            false /* is_sorted */, false /* is_hidden */});
+  schema.columns.push_back({"test1", SqlValue::Type::kLong, false /* is_id */,
+                            true /* is_sorted */, false /* is_hidden */});
+  schema.columns.push_back({"test2", SqlValue::Type::kLong, false /* is_id */,
+                            false /* is_sorted */, false /* is_hidden */});
+  schema.columns.push_back({"test3", SqlValue::Type::kLong, false /* is_id */,
+                            false /* is_sorted */, false /* is_hidden */});
+  return schema;
+}
 
 TEST(DbSqliteTable, IdEqCheaperThanOtherEq) {
-  TestTable table(1234);
+  auto schema = CreateSchema();
+  constexpr uint32_t kRowCount = 1234;
 
   QueryConstraints id_eq;
   id_eq.AddConstraint(0u, SQLITE_INDEX_CONSTRAINT_EQ, 0u);
 
-  auto id_cost = DbSqliteTable::EstimateCost(table, id_eq);
+  auto id_cost = DbSqliteTable::EstimateCost(schema, kRowCount, id_eq);
 
   QueryConstraints a_eq;
   a_eq.AddConstraint(1u, SQLITE_INDEX_CONSTRAINT_EQ, 1u);
 
-  auto a_cost = DbSqliteTable::EstimateCost(table, a_eq);
+  auto a_cost = DbSqliteTable::EstimateCost(schema, kRowCount, a_eq);
 
   ASSERT_LT(id_cost.cost, a_cost.cost);
   ASSERT_LT(id_cost.rows, a_cost.rows);
 }
 
 TEST(DbSqliteTable, IdEqCheaperThatOtherConstraint) {
-  TestTable table(1234);
+  auto schema = CreateSchema();
+  constexpr uint32_t kRowCount = 1234;
 
   QueryConstraints id_eq;
   id_eq.AddConstraint(0u, SQLITE_INDEX_CONSTRAINT_EQ, 0u);
 
-  auto id_cost = DbSqliteTable::EstimateCost(table, id_eq);
+  auto id_cost = DbSqliteTable::EstimateCost(schema, kRowCount, id_eq);
 
   QueryConstraints a_eq;
   a_eq.AddConstraint(1u, SQLITE_INDEX_CONSTRAINT_LT, 1u);
 
-  auto a_cost = DbSqliteTable::EstimateCost(table, a_eq);
+  auto a_cost = DbSqliteTable::EstimateCost(schema, kRowCount, a_eq);
 
   ASSERT_LT(id_cost.cost, a_cost.cost);
   ASSERT_LT(id_cost.rows, a_cost.rows);
 }
 
 TEST(DbSqliteTable, SingleEqCheaperThanMultipleConstraint) {
-  TestTable table(1234);
+  auto schema = CreateSchema();
+  constexpr uint32_t kRowCount = 1234;
 
   QueryConstraints single_eq;
   single_eq.AddConstraint(1u, SQLITE_INDEX_CONSTRAINT_EQ, 0u);
 
-  auto single_cost = DbSqliteTable::EstimateCost(table, single_eq);
+  auto single_cost = DbSqliteTable::EstimateCost(schema, kRowCount, single_eq);
 
   QueryConstraints multi_eq;
   multi_eq.AddConstraint(1u, SQLITE_INDEX_CONSTRAINT_EQ, 0u);
   multi_eq.AddConstraint(2u, SQLITE_INDEX_CONSTRAINT_EQ, 1u);
 
-  auto multi_cost = DbSqliteTable::EstimateCost(table, multi_eq);
+  auto multi_cost = DbSqliteTable::EstimateCost(schema, kRowCount, multi_eq);
 
   // The cost of the single filter should be cheaper (because of our special
   // handling of single equality). But the number of rows should be greater.
@@ -102,19 +96,21 @@
 }
 
 TEST(DbSqliteTable, MultiSortedEqCheaperThanMultiUnsortedEq) {
-  TestTable table(1234);
+  auto schema = CreateSchema();
+  constexpr uint32_t kRowCount = 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);
+  auto sorted_cost = DbSqliteTable::EstimateCost(schema, kRowCount, 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);
+  auto unsorted_cost =
+      DbSqliteTable::EstimateCost(schema, kRowCount, unsorted_eq);
 
   // The number of rows should be the same but the cost of the sorted
   // query should be less.
@@ -123,34 +119,35 @@
 }
 
 TEST(DbSqliteTable, EmptyTableCosting) {
-  TestTable table(0u);
+  auto schema = CreateSchema();
 
   QueryConstraints id_eq;
   id_eq.AddConstraint(0u, SQLITE_INDEX_CONSTRAINT_EQ, 0u);
 
-  auto id_cost = DbSqliteTable::EstimateCost(table, id_eq);
+  auto id_cost = DbSqliteTable::EstimateCost(schema, 0, id_eq);
 
   QueryConstraints a_eq;
   a_eq.AddConstraint(1u, SQLITE_INDEX_CONSTRAINT_LT, 1u);
 
-  auto a_cost = DbSqliteTable::EstimateCost(table, a_eq);
+  auto a_cost = DbSqliteTable::EstimateCost(schema, 0, a_eq);
 
   ASSERT_DOUBLE_EQ(id_cost.cost, a_cost.cost);
   ASSERT_EQ(id_cost.rows, a_cost.rows);
 }
 
 TEST(DbSqliteTable, OrderByOnSortedCheaper) {
-  TestTable table(1234);
+  auto schema = CreateSchema();
+  constexpr uint32_t kRowCount = 1234;
 
   QueryConstraints a_qc;
   a_qc.AddOrderBy(1u, false);
 
-  auto a_cost = DbSqliteTable::EstimateCost(table, a_qc);
+  auto a_cost = DbSqliteTable::EstimateCost(schema, kRowCount, a_qc);
 
   // On an ordered column, the constraint for sorting would get pruned so
   // we would end up with an empty constraint set.
   QueryConstraints sorted_qc;
-  auto sorted_cost = DbSqliteTable::EstimateCost(table, sorted_qc);
+  auto sorted_cost = DbSqliteTable::EstimateCost(schema, kRowCount, sorted_qc);
 
   ASSERT_LT(sorted_cost.cost, a_cost.cost);
   ASSERT_EQ(sorted_cost.rows, a_cost.rows);
diff --git a/src/trace_processor/sqlite/query_cache.h b/src/trace_processor/sqlite/query_cache.h
new file mode 100644
index 0000000..16a2b6e
--- /dev/null
+++ b/src/trace_processor/sqlite/query_cache.h
@@ -0,0 +1,81 @@
+/*
+ * 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_QUERY_CACHE_H_
+#define SRC_TRACE_PROCESSOR_SQLITE_QUERY_CACHE_H_
+
+#include "perfetto/ext/base/optional.h"
+
+#include "src/trace_processor/db/table.h"
+#include "src/trace_processor/sqlite/query_constraints.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Implements a simple caching strategy for commonly executed queries.
+// TODO(lalitm): the design of this class is very experimental. It was mainly
+// introduced to solve a specific problem (slow process summary tracks in the
+// Perfetto UI) and should not be modified without a full design discussion.
+class QueryCache {
+ public:
+  using Constraint = QueryConstraints::Constraint;
+
+  // Returns a cached table if the passed query set are currenly cached or
+  // nullptr otherwise.
+  std::shared_ptr<Table> GetIfCached(const Table* source,
+                                     const std::vector<Constraint>& cs) const {
+    if (cached_.source != source || cs.size() != cached_.constraints.size())
+      return nullptr;
+
+    auto p = [](const Constraint& a, const Constraint& b) {
+      return a.column == b.column && a.op == b.op;
+    };
+    bool same_cs =
+        std::equal(cs.begin(), cs.end(), cached_.constraints.begin(), p);
+    return same_cs ? cached_.table : nullptr;
+  }
+
+  // Caches the table with the given source, constraint and order set. Returns
+  // a pointer to the newly cached table.
+  std::shared_ptr<Table> GetOrCache(
+      const Table* source,
+      const std::vector<QueryConstraints::Constraint>& cs,
+      std::function<Table()> fn) {
+    std::shared_ptr<Table> cached = GetIfCached(source, cs);
+    if (cached)
+      return cached;
+
+    cached_.source = source;
+    cached_.constraints = cs;
+    cached_.table.reset(new Table(fn()));
+    return cached_.table;
+  }
+
+ private:
+  struct CachedTable {
+    std::shared_ptr<Table> table;
+
+    const Table* source = nullptr;
+    std::vector<Constraint> constraints;
+  };
+
+  CachedTable cached_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_SQLITE_QUERY_CACHE_H_
diff --git a/src/trace_processor/span_join_operator_table.cc b/src/trace_processor/sqlite/span_join_operator_table.cc
similarity index 96%
rename from src/trace_processor/span_join_operator_table.cc
rename to src/trace_processor/sqlite/span_join_operator_table.cc
index 1cb258d..fd9d897 100644
--- a/src/trace_processor/span_join_operator_table.cc
+++ b/src/trace_processor/sqlite/span_join_operator_table.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/span_join_operator_table.h"
+#include "src/trace_processor/sqlite/span_join_operator_table.h"
 
 #include <sqlite3.h>
 #include <string.h>
@@ -197,9 +197,23 @@
                                      BestIndexInfo* info) {
   // TODO(lalitm): figure out cost estimation.
   const auto& ob = qc.order_by();
-  if (ob.size() == 1 && ob.front().iColumn == Column::kTimestamp &&
-      !ob.front().desc) {
-    info->sqlite_omit_order_by = true;
+
+  if (partitioning_ == PartitioningType::kNoPartitioning) {
+    // If both tables are not partitioned and we have a single order by on ts,
+    // we return data in the correct order.
+    info->sqlite_omit_order_by =
+        ob.size() == 1 && ob[0].iColumn == Column::kTimestamp && !ob[0].desc;
+  } else {
+    // If one of the tables is partitioned, and we have an order by on the
+    // partition column followed (optionally) by an order by on timestamp, we
+    // return data in the correct order.
+    bool is_first_ob_partition =
+        ob.size() >= 1 && ob[0].iColumn == Column::kPartition && !ob[0].desc;
+    bool is_second_ob_ts =
+        ob.size() >= 2 && ob[1].iColumn == Column::kTimestamp && !ob[1].desc;
+    info->sqlite_omit_order_by =
+        (ob.size() == 1 && is_first_ob_partition) ||
+        (ob.size() == 2 && is_first_ob_partition && is_second_ob_ts);
   }
   return SQLITE_OK;
 }
diff --git a/src/trace_processor/span_join_operator_table.h b/src/trace_processor/sqlite/span_join_operator_table.h
similarity index 98%
rename from src/trace_processor/span_join_operator_table.h
rename to src/trace_processor/sqlite/span_join_operator_table.h
index 3f9ec18..290b3d9 100644
--- a/src/trace_processor/span_join_operator_table.h
+++ b/src/trace_processor/sqlite/span_join_operator_table.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_SPAN_JOIN_OPERATOR_TABLE_H_
-#define SRC_TRACE_PROCESSOR_SPAN_JOIN_OPERATOR_TABLE_H_
+#ifndef SRC_TRACE_PROCESSOR_SQLITE_SPAN_JOIN_OPERATOR_TABLE_H_
+#define SRC_TRACE_PROCESSOR_SQLITE_SPAN_JOIN_OPERATOR_TABLE_H_
 
 #include <sqlite3.h>
 
@@ -425,4 +425,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_SPAN_JOIN_OPERATOR_TABLE_H_
+#endif  // SRC_TRACE_PROCESSOR_SQLITE_SPAN_JOIN_OPERATOR_TABLE_H_
diff --git a/src/trace_processor/span_join_operator_table_unittest.cc b/src/trace_processor/sqlite/span_join_operator_table_unittest.cc
similarity index 96%
rename from src/trace_processor/span_join_operator_table_unittest.cc
rename to src/trace_processor/sqlite/span_join_operator_table_unittest.cc
index 503360e..87bb645 100644
--- a/src/trace_processor/span_join_operator_table_unittest.cc
+++ b/src/trace_processor/sqlite/span_join_operator_table_unittest.cc
@@ -14,10 +14,8 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/span_join_operator_table.h"
+#include "src/trace_processor/sqlite/span_join_operator_table.h"
 
-#include "src/trace_processor/trace_processor_context.h"
-#include "src/trace_processor/trace_storage.h"
 #include "test/gtest_and_gmock.h"
 
 namespace perfetto {
@@ -32,9 +30,7 @@
     PERFETTO_CHECK(sqlite3_open(":memory:", &db) == SQLITE_OK);
     db_.reset(db);
 
-    context_.storage.reset(new TraceStorage());
-
-    SpanJoinOperatorTable::RegisterTable(db_.get(), context_.storage.get());
+    SpanJoinOperatorTable::RegisterTable(db_.get(), nullptr);
   }
 
   void PrepareValidStatement(const std::string& sql) {
@@ -59,7 +55,6 @@
   }
 
  protected:
-  TraceProcessorContext context_;
   ScopedDb db_;
   ScopedStmt stmt_;
 };
diff --git a/src/trace_processor/sql_stats_table.cc b/src/trace_processor/sqlite/sql_stats_table.cc
similarity index 96%
rename from src/trace_processor/sql_stats_table.cc
rename to src/trace_processor/sqlite/sql_stats_table.cc
index 11da41b..b9fb2db 100644
--- a/src/trace_processor/sql_stats_table.cc
+++ b/src/trace_processor/sqlite/sql_stats_table.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/sql_stats_table.h"
+#include "src/trace_processor/sqlite/sql_stats_table.h"
 
 #include <sqlite3.h>
 
@@ -23,7 +23,7 @@
 #include <numeric>
 
 #include "src/trace_processor/sqlite/sqlite_utils.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/trace_storage.h"
 
 namespace perfetto {
 namespace trace_processor {
diff --git a/src/trace_processor/sql_stats_table.h b/src/trace_processor/sqlite/sql_stats_table.h
similarity index 93%
rename from src/trace_processor/sql_stats_table.h
rename to src/trace_processor/sqlite/sql_stats_table.h
index 7793f2e..c146ee8 100644
--- a/src/trace_processor/sql_stats_table.h
+++ b/src/trace_processor/sqlite/sql_stats_table.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_SQL_STATS_TABLE_H_
-#define SRC_TRACE_PROCESSOR_SQL_STATS_TABLE_H_
+#ifndef SRC_TRACE_PROCESSOR_SQLITE_SQL_STATS_TABLE_H_
+#define SRC_TRACE_PROCESSOR_SQLITE_SQL_STATS_TABLE_H_
 
 #include <limits>
 #include <memory>
@@ -83,4 +83,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_SQL_STATS_TABLE_H_
+#endif  // SRC_TRACE_PROCESSOR_SQLITE_SQL_STATS_TABLE_H_
diff --git a/src/trace_processor/sqlite/sqlite_raw_table.cc b/src/trace_processor/sqlite/sqlite_raw_table.cc
new file mode 100644
index 0000000..ee7d33e
--- /dev/null
+++ b/src/trace_processor/sqlite/sqlite_raw_table.cc
@@ -0,0 +1,591 @@
+/*
+ * 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/trace_processor/sqlite/sqlite_raw_table.h"
+
+#include <inttypes.h>
+
+#include "perfetto/base/compiler.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "src/trace_processor/importers/ftrace/ftrace_descriptors.h"
+#include "src/trace_processor/sqlite/sqlite_utils.h"
+#include "src/trace_processor/types/gfp_flags.h"
+#include "src/trace_processor/types/task_state.h"
+#include "src/trace_processor/types/variadic.h"
+
+#include "protos/perfetto/trace/ftrace/binder.pbzero.h"
+#include "protos/perfetto/trace/ftrace/clk.pbzero.h"
+#include "protos/perfetto/trace/ftrace/filemap.pbzero.h"
+#include "protos/perfetto/trace/ftrace/ftrace.pbzero.h"
+#include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
+#include "protos/perfetto/trace/ftrace/power.pbzero.h"
+#include "protos/perfetto/trace/ftrace/sched.pbzero.h"
+#include "protos/perfetto/trace/ftrace/workqueue.pbzero.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+namespace {
+std::tuple<uint32_t, uint32_t> ParseKernelReleaseVersion(
+    base::StringView system_release) {
+  size_t first_dot_pos = system_release.find(".");
+  size_t second_dot_pos = system_release.find(".", first_dot_pos + 1);
+  auto major_version = base::StringToUInt32(
+      system_release.substr(0, first_dot_pos).ToStdString());
+  auto minor_version = base::StringToUInt32(
+      system_release
+          .substr(first_dot_pos + 1, second_dot_pos - (first_dot_pos + 1))
+          .ToStdString());
+  return std::make_tuple(major_version.value(), minor_version.value());
+}
+
+struct FtraceTime {
+  FtraceTime(int64_t ns)
+      : secs(ns / 1000000000LL), micros((ns - secs * 1000000000LL) / 1000) {}
+
+  const int64_t secs;
+  const int64_t micros;
+};
+
+class ArgsSerializer {
+ public:
+  ArgsSerializer(const TraceStorage*,
+                 ArgSetId arg_set_id,
+                 NullTermStringView event_name,
+                 std::vector<uint32_t>* field_id_to_arg_index,
+                 base::StringWriter*);
+
+  void SerializeArgs();
+
+ private:
+  using ValueWriter = std::function<void(const Variadic&)>;
+
+  void WriteArgForField(uint32_t field_id) {
+    WriteArgForField(field_id,
+                     [this](const Variadic& v) { return WriteValue(v); });
+  }
+
+  void WriteArgForField(uint32_t field_id, ValueWriter writer) {
+    WriteArgAtRow(FieldIdToRow(field_id), writer);
+  }
+
+  void WriteValueForField(uint32_t field_id) {
+    WriteValueForField(field_id,
+                       [this](const Variadic& v) { return WriteValue(v); });
+  }
+
+  void WriteValueForField(uint32_t field_id, ValueWriter writer) {
+    writer(storage_->GetArgValue(FieldIdToRow(field_id)));
+  }
+
+  void WriteArgAtRow(uint32_t arg_index) {
+    WriteArgAtRow(arg_index,
+                  [this](const Variadic& v) { return WriteValue(v); });
+  }
+
+  void WriteArgAtRow(uint32_t arg_index, ValueWriter writer);
+
+  void WriteValue(const Variadic& variadic);
+
+  bool ParseGfpFlags(Variadic value);
+
+  uint32_t FieldIdToRow(uint32_t field_id) {
+    PERFETTO_DCHECK(field_id > 0);
+    PERFETTO_DCHECK(field_id < field_id_to_arg_index_->size());
+    uint32_t index_in_arg_set = (*field_id_to_arg_index_)[field_id];
+    return start_row_ + index_in_arg_set;
+  }
+
+  const TraceStorage* storage_ = nullptr;
+  ArgSetId arg_set_id_ = kInvalidArgSetId;
+  NullTermStringView event_name_;
+  std::vector<uint32_t>* field_id_to_arg_index_;
+
+  RowMap row_map_;
+  uint32_t start_row_ = 0;
+
+  base::StringWriter* writer_ = nullptr;
+};
+
+ArgsSerializer::ArgsSerializer(const TraceStorage* storage,
+                               ArgSetId arg_set_id,
+                               NullTermStringView event_name,
+                               std::vector<uint32_t>* field_id_to_arg_index,
+                               base::StringWriter* writer)
+    : storage_(storage),
+      arg_set_id_(arg_set_id),
+      event_name_(event_name),
+      field_id_to_arg_index_(field_id_to_arg_index),
+      writer_(writer) {
+  const auto& args = storage_->arg_table();
+  const auto& set_ids = args.arg_set_id();
+
+  // We assume that the row map is a contiguous range (which is always the case
+  // because arg_set_ids are contiguous by definition).
+  row_map_ = args.FilterToRowMap({set_ids.eq(arg_set_id_)});
+  start_row_ = row_map_.empty() ? 0 : row_map_.Get(0);
+
+  // If the vector already has entries, we've previously cached the mapping
+  // from field id to arg index.
+  if (!field_id_to_arg_index->empty())
+    return;
+
+  auto* descriptor = GetMessageDescriptorForName(event_name);
+  if (!descriptor) {
+    // If we don't have a descriptor, this event must be a generic ftrace event.
+    // As we can't possibly have any special handling for generic events, just
+    // add a row to the vector (for the invalid field id 0) to remove future
+    // lookups for this event name.
+    field_id_to_arg_index->resize(1);
+    return;
+  }
+
+  // If we have a descriptor, try and create the mapping from proto field id
+  // to the index in the arg set.
+  size_t max = descriptor->max_field_id;
+
+  // We need to reserve an index for the invalid field id 0.
+  field_id_to_arg_index_->resize(max + 1);
+
+  // Go through each field id and find the entry in the args table for that
+  for (uint32_t i = 1; i <= max; ++i) {
+    for (auto it = row_map_.IterateRows(); it; it.Next()) {
+      base::StringView key = args.key().GetString(it.row());
+      if (key == descriptor->fields[i].name) {
+        (*field_id_to_arg_index)[i] = it.index();
+        break;
+      }
+    }
+  }
+}
+
+void ArgsSerializer::SerializeArgs() {
+  if (row_map_.empty())
+    return;
+
+  if (event_name_ == "sched_switch") {
+    using SS = protos::pbzero::SchedSwitchFtraceEvent;
+
+    WriteArgForField(SS::kPrevCommFieldNumber);
+    WriteArgForField(SS::kPrevPidFieldNumber);
+    WriteArgForField(SS::kPrevPrioFieldNumber);
+    WriteArgForField(SS::kPrevStateFieldNumber, [this](const Variadic& value) {
+      PERFETTO_DCHECK(value.type == Variadic::Type::kInt);
+      auto state = static_cast<uint16_t>(value.int_value);
+      writer_->AppendString(
+          ftrace_utils::TaskState(state).ToString('|').data());
+    });
+    writer_->AppendLiteral(" ==>");
+    WriteArgForField(SS::kNextCommFieldNumber);
+    WriteArgForField(SS::kNextPidFieldNumber);
+    WriteArgForField(SS::kNextPrioFieldNumber);
+    return;
+  } else if (event_name_ == "sched_wakeup") {
+    using SW = protos::pbzero::SchedWakeupFtraceEvent;
+    WriteArgForField(SW::kCommFieldNumber);
+    WriteArgForField(SW::kPidFieldNumber);
+    WriteArgForField(SW::kPrioFieldNumber);
+    WriteArgForField(SW::kTargetCpuFieldNumber, [this](const Variadic& value) {
+      PERFETTO_DCHECK(value.type == Variadic::Type::kInt);
+      writer_->AppendPaddedInt<'0', 3>(value.int_value);
+    });
+    return;
+  } else if (event_name_ == "clock_set_rate") {
+    using CSR = protos::pbzero::ClockSetRateFtraceEvent;
+
+    // We use the string "todo" as the name to stay consistent with old
+    // trace_to_text print code.
+    writer_->AppendString(" todo");
+    WriteArgForField(CSR::kStateFieldNumber);
+    WriteArgForField(CSR::kCpuIdFieldNumber);
+    return;
+  } else if (event_name_ == "clk_set_rate") {
+    using CSR = protos::pbzero::ClkSetRateFtraceEvent;
+    writer_->AppendLiteral(" ");
+    WriteValueForField(CSR::kNameFieldNumber);
+    writer_->AppendLiteral(" ");
+    WriteValueForField(CSR::kRateFieldNumber);
+    return;
+  } else if (event_name_ == "clock_enable") {
+    using CE = protos::pbzero::ClockEnableFtraceEvent;
+    WriteValueForField(CE::kNameFieldNumber);
+    WriteArgForField(CE::kStateFieldNumber);
+    WriteArgForField(CE::kCpuIdFieldNumber);
+    return;
+  } else if (event_name_ == "clock_disable") {
+    using CD = protos::pbzero::ClockDisableFtraceEvent;
+    WriteValueForField(CD::kNameFieldNumber);
+    WriteArgForField(CD::kStateFieldNumber);
+    WriteArgForField(CD::kCpuIdFieldNumber);
+    return;
+  } else if (event_name_ == "binder_transaction") {
+    using BT = protos::pbzero::BinderTransactionFtraceEvent;
+    writer_->AppendString(" transaction=");
+    WriteValueForField(BT::kDebugIdFieldNumber, [this](const Variadic& value) {
+      PERFETTO_DCHECK(value.type == Variadic::Type::kInt);
+      writer_->AppendUnsignedInt(static_cast<uint32_t>(value.int_value));
+    });
+
+    writer_->AppendString(" dest_node=");
+    WriteValueForField(
+        BT::kTargetNodeFieldNumber, [this](const Variadic& value) {
+          PERFETTO_DCHECK(value.type == Variadic::Type::kInt);
+          writer_->AppendUnsignedInt(static_cast<uint32_t>(value.int_value));
+        });
+
+    writer_->AppendString(" dest_proc=");
+    WriteValueForField(BT::kToProcFieldNumber);
+
+    writer_->AppendString(" dest_thread=");
+    WriteValueForField(BT::kToThreadFieldNumber);
+
+    writer_->AppendString(" reply=");
+    WriteValueForField(BT::kReplyFieldNumber);
+
+    writer_->AppendString(" flags=0x");
+    WriteValueForField(BT::kFlagsFieldNumber, [this](const Variadic& value) {
+      PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
+      writer_->AppendHexInt(value.uint_value);
+    });
+
+    writer_->AppendString(" code=0x");
+    WriteValueForField(BT::kCodeFieldNumber, [this](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;
+    writer_->AppendString(" transaction=");
+    WriteValueForField(
+        BTAB::kDebugIdFieldNumber, [this](const Variadic& value) {
+          PERFETTO_DCHECK(value.type == Variadic::Type::kInt);
+          writer_->AppendUnsignedInt(static_cast<uint32_t>(value.int_value));
+        });
+    WriteArgForField(BTAB::kDataSizeFieldNumber);
+    WriteArgForField(BTAB::kOffsetsSizeFieldNumber);
+    return;
+  } else if (event_name_ == "binder_transaction_received") {
+    using BTR = protos::pbzero::BinderTransactionReceivedFtraceEvent;
+    writer_->AppendString(" transaction=");
+    WriteValueForField(BTR::kDebugIdFieldNumber, [this](const Variadic& value) {
+      PERFETTO_DCHECK(value.type == Variadic::Type::kInt);
+      writer_->AppendUnsignedInt(static_cast<uint32_t>(value.int_value));
+    });
+    return;
+  } else if (event_name_ == "mm_filemap_add_to_page_cache") {
+    using MFA = protos::pbzero::MmFilemapAddToPageCacheFtraceEvent;
+    writer_->AppendString(" dev ");
+    WriteValueForField(MFA::kSDevFieldNumber, [this](const Variadic& value) {
+      PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
+      writer_->AppendUnsignedInt(value.uint_value >> 20);
+    });
+    writer_->AppendString(":");
+    WriteValueForField(MFA::kSDevFieldNumber, [this](const Variadic& value) {
+      PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
+      writer_->AppendUnsignedInt(value.uint_value & ((1 << 20) - 1));
+    });
+    writer_->AppendString(" ino ");
+    WriteValueForField(MFA::kIInoFieldNumber, [this](const Variadic& value) {
+      PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
+      writer_->AppendHexInt(value.uint_value);
+    });
+    writer_->AppendString(" page=0000000000000000");
+    writer_->AppendString(" pfn=");
+    WriteValueForField(MFA::kPfnFieldNumber);
+    writer_->AppendString(" ofs=");
+    WriteValueForField(MFA::kIndexFieldNumber, [this](const Variadic& value) {
+      PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
+      writer_->AppendUnsignedInt(value.uint_value << 12);
+    });
+    return;
+  } else if (event_name_ == "print") {
+    using P = protos::pbzero::PrintFtraceEvent;
+
+    writer_->AppendChar(' ');
+    WriteValueForField(P::kBufFieldNumber, [this](const Variadic& value) {
+      PERFETTO_DCHECK(value.type == Variadic::Type::kString);
+
+      NullTermStringView 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'
+                                ? str.size() - 1
+                                : str.size();
+      writer_->AppendString(str.c_str(), chars_to_print);
+    });
+    return;
+  } else if (event_name_ == "sched_blocked_reason") {
+    using SBR = protos::pbzero::SchedBlockedReasonFtraceEvent;
+    WriteArgForField(SBR::kPidFieldNumber);
+    WriteArgForField(SBR::kIoWaitFieldNumber);
+    WriteArgForField(SBR::kCallerFieldNumber, [this](const Variadic& value) {
+      PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
+      writer_->AppendHexInt(value.uint_value);
+    });
+    return;
+  } else if (event_name_ == "workqueue_activate_work") {
+    using WAW = protos::pbzero::WorkqueueActivateWorkFtraceEvent;
+    writer_->AppendString(" work struct ");
+    WriteValueForField(WAW::kWorkFieldNumber, [this](const Variadic& value) {
+      PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
+      writer_->AppendHexInt(value.uint_value);
+    });
+    return;
+  } else if (event_name_ == "workqueue_execute_start") {
+    using WES = protos::pbzero::WorkqueueExecuteStartFtraceEvent;
+    writer_->AppendString(" work struct ");
+    WriteValueForField(WES::kWorkFieldNumber, [this](const Variadic& value) {
+      PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
+      writer_->AppendHexInt(value.uint_value);
+    });
+    writer_->AppendString(": function ");
+    WriteValueForField(WES::kFunctionFieldNumber,
+                       [this](const Variadic& value) {
+                         PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
+                         writer_->AppendHexInt(value.uint_value);
+                       });
+    return;
+  } else if (event_name_ == "workqueue_execute_end") {
+    using WE = protos::pbzero::WorkqueueExecuteEndFtraceEvent;
+    writer_->AppendString(" work struct ");
+    WriteValueForField(WE::kWorkFieldNumber, [this](const Variadic& value) {
+      PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
+      writer_->AppendHexInt(value.uint_value);
+    });
+    return;
+  } else if (event_name_ == "workqueue_queue_work") {
+    using WQW = protos::pbzero::WorkqueueQueueWorkFtraceEvent;
+    writer_->AppendString(" work struct=");
+    WriteValueForField(WQW::kWorkFieldNumber, [this](const Variadic& value) {
+      PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
+      writer_->AppendHexInt(value.uint_value);
+    });
+    WriteArgForField(WQW::kFunctionFieldNumber, [this](const Variadic& value) {
+      PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
+      writer_->AppendHexInt(value.uint_value);
+    });
+    WriteArgForField(WQW::kWorkqueueFieldNumber, [this](const Variadic& value) {
+      PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
+      writer_->AppendHexInt(value.uint_value);
+    });
+    WriteValueForField(WQW::kReqCpuFieldNumber);
+    WriteValueForField(WQW::kCpuFieldNumber);
+    return;
+  }
+
+  for (auto it = row_map_.IterateRows(); it; it.Next()) {
+    WriteArgAtRow(it.row());
+  }
+}
+
+void ArgsSerializer::WriteArgAtRow(uint32_t arg_row, ValueWriter writer) {
+  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(value))
+    return;
+  writer(value);
+}
+
+void ArgsSerializer::WriteValue(const Variadic& value) {
+  switch (value.type) {
+    case Variadic::kInt:
+      writer_->AppendInt(value.int_value);
+      break;
+    case Variadic::kUint:
+      writer_->AppendUnsignedInt(value.uint_value);
+      break;
+    case Variadic::kString: {
+      const auto& str = storage_->GetString(value.string_value);
+      writer_->AppendString(str.c_str(), str.size());
+      break;
+    }
+    case Variadic::kReal:
+      writer_->AppendDouble(value.real_value);
+      break;
+    case Variadic::kPointer:
+      writer_->AppendUnsignedInt(value.pointer_value);
+      break;
+    case Variadic::kBool:
+      writer_->AppendBool(value.bool_value);
+      break;
+    case Variadic::kJson: {
+      const auto& str = storage_->GetString(value.json_value);
+      writer_->AppendString(str.c_str(), str.size());
+      break;
+    }
+  }
+}
+
+bool ArgsSerializer::ParseGfpFlags(Variadic value) {
+  const auto& metadata_table = storage_->metadata_table();
+
+  auto opt_name_idx = metadata_table.name().IndexOf(
+      metadata::kNames[metadata::KeyIDs::system_name]);
+  auto opt_release_idx = metadata_table.name().IndexOf(
+      metadata::kNames[metadata::KeyIDs::system_release]);
+  if (!opt_name_idx || !opt_release_idx)
+    return false;
+
+  const auto& str_value = metadata_table.str_value();
+  base::StringView system_name = str_value.GetString(*opt_name_idx);
+  if (system_name != "Linux")
+    return false;
+
+  base::StringView system_release = str_value.GetString(*opt_release_idx);
+  auto version = ParseKernelReleaseVersion(system_release);
+
+  WriteGfpFlag(value.uint_value, version, writer_);
+  return true;
+}
+
+}  // namespace
+
+SqliteRawTable::SqliteRawTable(sqlite3* db, Context context)
+    : DbSqliteTable(
+          db,
+          {context.cache, tables::RawTable::Schema(), TableComputation::kStatic,
+           &context.storage->raw_table(), nullptr}),
+      serializer_(context.storage) {
+  auto fn = [](sqlite3_context* ctx, int argc, sqlite3_value** argv) {
+    auto* thiz = static_cast<SqliteRawTable*>(sqlite3_user_data(ctx));
+    thiz->ToSystrace(ctx, argc, argv);
+  };
+  sqlite3_create_function(db, "to_ftrace", 1,
+                          SQLITE_UTF8 | SQLITE_DETERMINISTIC, this, fn, nullptr,
+                          nullptr);
+}
+
+SqliteRawTable::~SqliteRawTable() = default;
+
+void SqliteRawTable::RegisterTable(sqlite3* db,
+                                   QueryCache* cache,
+                                   const TraceStorage* storage) {
+  SqliteTable::Register<SqliteRawTable, Context>(db, Context{cache, storage},
+                                                 "raw");
+}
+
+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]));
+
+  auto str = serializer_.SerializeToString(row);
+  sqlite3_result_text(ctx, str.release(), -1, free);
+}
+
+SystraceSerializer::SystraceSerializer(const TraceStorage* storage)
+    : storage_(storage) {}
+
+SystraceSerializer::ScopedCString SystraceSerializer::SerializeToString(
+    uint32_t raw_row) {
+  const auto& raw = storage_->raw_table();
+
+  char line[4096];
+  base::StringWriter writer(line, sizeof(line));
+
+  SerializePrefix(raw_row, &writer);
+
+  StringId event_name_id = raw.name()[raw_row];
+  NullTermStringView event_name = storage_->GetString(event_name_id);
+  writer.AppendChar(' ');
+  if (event_name == "print") {
+    writer.AppendString("tracing_mark_write");
+  } else {
+    writer.AppendString(event_name.c_str(), event_name.size());
+  }
+  writer.AppendChar(':');
+
+  ArgsSerializer serializer(storage_, raw.arg_set_id()[raw_row], event_name,
+                            &proto_id_to_arg_index_by_event_[event_name_id],
+                            &writer);
+  serializer.SerializeArgs();
+
+  return ScopedCString(writer.CreateStringCopy(), free);
+}
+
+void SystraceSerializer::SerializePrefix(uint32_t raw_row,
+                                         base::StringWriter* writer) {
+  const auto& raw = storage_->raw_table();
+
+  int64_t ts = raw.ts()[raw_row];
+  uint32_t cpu = raw.cpu()[raw_row];
+
+  UniqueTid utid = raw.utid()[raw_row];
+  uint32_t tid = storage_->thread_table().tid()[utid];
+
+  uint32_t tgid = 0;
+  auto opt_upid = storage_->thread_table().upid()[utid];
+  if (opt_upid.has_value()) {
+    tgid = storage_->process_table().pid()[*opt_upid];
+  }
+  auto name = storage_->GetString(storage_->thread_table().name()[utid]);
+
+  FtraceTime ftrace_time(ts);
+  if (tid == 0) {
+    name = "<idle>";
+  } else if (name == "") {
+    name = "<unknown>";
+  } else if (name == "CrRendererMain") {
+    // TODO(taylori): Remove this when crbug.com/978093 is fixed or
+    // when a better solution is found.
+    name = "CrRendererMainThread";
+  }
+
+  int64_t padding = 16 - static_cast<int64_t>(name.size());
+  if (padding > 0) {
+    writer->AppendChar(' ', static_cast<size_t>(padding));
+  }
+  for (size_t i = 0; i < name.size(); ++i) {
+    char c = name.data()[i];
+    writer->AppendChar(c == '-' ? '_' : c);
+  }
+  writer->AppendChar('-');
+
+  size_t pre_pid_pos = writer->pos();
+  writer->AppendInt(tid);
+  size_t pid_chars = writer->pos() - pre_pid_pos;
+  if (PERFETTO_LIKELY(pid_chars < 5)) {
+    writer->AppendChar(' ', 5 - pid_chars);
+  }
+
+  writer->AppendLiteral(" (");
+  if (tgid == 0) {
+    writer->AppendLiteral("-----");
+  } else {
+    writer->AppendPaddedInt<' ', 5>(tgid);
+  }
+  writer->AppendLiteral(") [");
+  writer->AppendPaddedInt<'0', 3>(cpu);
+  writer->AppendLiteral("] .... ");
+
+  writer->AppendInt(ftrace_time.secs);
+  writer->AppendChar('.');
+  writer->AppendPaddedInt<'0', 6>(ftrace_time.micros);
+  writer->AppendChar(':');
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/sqlite/sqlite_raw_table.h b/src/trace_processor/sqlite/sqlite_raw_table.h
new file mode 100644
index 0000000..4c8190e
--- /dev/null
+++ b/src/trace_processor/sqlite/sqlite_raw_table.h
@@ -0,0 +1,67 @@
+/*
+ * 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_SQLITE_SQLITE_RAW_TABLE_H_
+#define SRC_TRACE_PROCESSOR_SQLITE_SQLITE_RAW_TABLE_H_
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/string_writer.h"
+#include "src/trace_processor/sqlite/db_sqlite_table.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/variadic.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class SystraceSerializer {
+ public:
+  using ScopedCString = std::unique_ptr<char, void (*)(void*)>;
+
+  SystraceSerializer(const TraceStorage* storage);
+
+  ScopedCString SerializeToString(uint32_t raw_row);
+
+ private:
+  using StringIdMap = std::unordered_map<StringId, std::vector<uint32_t>>;
+
+  void SerializePrefix(uint32_t raw_row, base::StringWriter* writer);
+
+  StringIdMap proto_id_to_arg_index_by_event_;
+  const TraceStorage* storage_ = nullptr;
+};
+
+class SqliteRawTable : public DbSqliteTable {
+ public:
+  struct Context {
+    QueryCache* cache;
+    const TraceStorage* storage;
+  };
+
+  SqliteRawTable(sqlite3*, Context);
+  virtual ~SqliteRawTable();
+
+  static void RegisterTable(sqlite3* db, QueryCache*, const TraceStorage*);
+
+ private:
+  void ToSystrace(sqlite3_context* ctx, int argc, sqlite3_value** argv);
+
+  SystraceSerializer serializer_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_SQLITE_SQLITE_RAW_TABLE_H_
diff --git a/src/trace_processor/sqlite/sqlite_table.cc b/src/trace_processor/sqlite/sqlite_table.cc
index da6f553..a2ed4c5 100644
--- a/src/trace_processor/sqlite/sqlite_table.cc
+++ b/src/trace_processor/sqlite/sqlite_table.cc
@@ -85,6 +85,19 @@
   info.sqlite_omit_constraint.resize(qc.constraints().size());
 
   ret = BestIndex(qc, &info);
+
+  // Although the SQLite documentation promises that if we return
+  // SQLITE_CONSTRAINT, it won't chose this query plan, in practice, this causes
+  // the entire query to be abandonned even if there is another query plan which
+  // would definitely work. For this reason, we reserve idxNum == INT_MAX for
+  // invalid constraints and just keep the default estimated cost (which should
+  // lead to the plan not being chosen). In xFilter, we can then return
+  // SQLITE_CONSTRAINT if this query plan is still chosen.
+  if (ret == SQLITE_CONSTRAINT) {
+    idx->idxNum = kInvalidConstraintsInBestIndexNum;
+    return SQLITE_OK;
+  }
+
   if (ret != SQLITE_OK)
     return ret;
 
diff --git a/src/trace_processor/sqlite/sqlite_table.h b/src/trace_processor/sqlite/sqlite_table.h
index 2cb7a0f..99d9e29 100644
--- a/src/trace_processor/sqlite/sqlite_table.h
+++ b/src/trace_processor/sqlite/sqlite_table.h
@@ -20,6 +20,7 @@
 #include <sqlite3.h>
 
 #include <functional>
+#include <limits>
 #include <memory>
 #include <string>
 #include <vector>
@@ -212,7 +213,7 @@
     auto create_fn = [](sqlite3* xdb, void* arg, int argc,
                         const char* const* argv, sqlite3_vtab** tab,
                         char** pzErr) {
-      const auto* xdesc = static_cast<const TableDescriptor<Context>*>(arg);
+      auto* xdesc = static_cast<TableDescriptor<Context>*>(arg);
       auto table = xdesc->factory(xdb, std::move(xdesc->context));
       table->name_ = xdesc->name;
 
@@ -257,6 +258,13 @@
     };
     module->xFilter = [](sqlite3_vtab_cursor* vc, int i, const char* s, int a,
                          sqlite3_value** v) {
+      // If the idxNum is equal to kSqliteConstraintBestIndexNum, that means
+      // in BestIndexInternal, we tried to discourage the query planner from
+      // chosing this plan. As the subclass has informed us that it cannot
+      // handle this plan, just return the error now.
+      if (i == kInvalidConstraintsInBestIndexNum)
+        return SQLITE_CONSTRAINT;
+
       auto* c = static_cast<Cursor*>(vc);
       bool is_cached = c->table_->ReadConstraints(i, s, a);
 
@@ -333,6 +341,9 @@
   const std::string& name() const { return name_; }
 
  private:
+  static constexpr int kInvalidConstraintsInBestIndexNum =
+      std::numeric_limits<int>::max();
+
   template <typename TableType, typename Context>
   static Factory<Context> GetFactory() {
     return [](sqlite3* db, Context ctx) {
diff --git a/src/trace_processor/stats_table.cc b/src/trace_processor/sqlite/stats_table.cc
similarity index 98%
rename from src/trace_processor/stats_table.cc
rename to src/trace_processor/sqlite/stats_table.cc
index ab4599a..de536a5 100644
--- a/src/trace_processor/stats_table.cc
+++ b/src/trace_processor/sqlite/stats_table.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/stats_table.h"
+#include "src/trace_processor/sqlite/stats_table.h"
 
 #include "src/trace_processor/sqlite/sqlite_utils.h"
 
diff --git a/src/trace_processor/stats_table.h b/src/trace_processor/sqlite/stats_table.h
similarity index 89%
rename from src/trace_processor/stats_table.h
rename to src/trace_processor/sqlite/stats_table.h
index 3a25413..6ef8c20 100644
--- a/src/trace_processor/stats_table.h
+++ b/src/trace_processor/sqlite/stats_table.h
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_STATS_TABLE_H_
-#define SRC_TRACE_PROCESSOR_STATS_TABLE_H_
+#ifndef SRC_TRACE_PROCESSOR_SQLITE_STATS_TABLE_H_
+#define SRC_TRACE_PROCESSOR_SQLITE_STATS_TABLE_H_
 
 #include <limits>
 #include <memory>
 
 #include "src/trace_processor/sqlite/sqlite_table.h"
-#include "src/trace_processor/stats.h"
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/stats.h"
+#include "src/trace_processor/storage/trace_storage.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -73,4 +73,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_STATS_TABLE_H_
+#endif  // SRC_TRACE_PROCESSOR_SQLITE_STATS_TABLE_H_
diff --git a/src/trace_processor/window_operator_table.cc b/src/trace_processor/sqlite/window_operator_table.cc
similarity index 98%
rename from src/trace_processor/window_operator_table.cc
rename to src/trace_processor/sqlite/window_operator_table.cc
index 1d801f8..2c7b543 100644
--- a/src/trace_processor/window_operator_table.cc
+++ b/src/trace_processor/sqlite/window_operator_table.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/window_operator_table.h"
+#include "src/trace_processor/sqlite/window_operator_table.h"
 
 #include "src/trace_processor/sqlite/sqlite_utils.h"
 
diff --git a/src/trace_processor/window_operator_table.h b/src/trace_processor/sqlite/window_operator_table.h
similarity index 93%
rename from src/trace_processor/window_operator_table.h
rename to src/trace_processor/sqlite/window_operator_table.h
index 504ec13..c0001c6 100644
--- a/src/trace_processor/window_operator_table.h
+++ b/src/trace_processor/sqlite/window_operator_table.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_WINDOW_OPERATOR_TABLE_H_
-#define SRC_TRACE_PROCESSOR_WINDOW_OPERATOR_TABLE_H_
+#ifndef SRC_TRACE_PROCESSOR_SQLITE_WINDOW_OPERATOR_TABLE_H_
+#define SRC_TRACE_PROCESSOR_SQLITE_WINDOW_OPERATOR_TABLE_H_
 
 #include <limits>
 #include <memory>
@@ -94,4 +94,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_WINDOW_OPERATOR_TABLE_H_
+#endif  // SRC_TRACE_PROCESSOR_SQLITE_WINDOW_OPERATOR_TABLE_H_
diff --git a/src/trace_processor/sqlite_experimental_flamegraph_table.cc b/src/trace_processor/sqlite_experimental_flamegraph_table.cc
deleted file mode 100644
index 12a9bb6..0000000
--- a/src/trace_processor/sqlite_experimental_flamegraph_table.cc
+++ /dev/null
@@ -1,178 +0,0 @@
-
-/*
- * 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
deleted file mode 100644
index e531814..0000000
--- a/src/trace_processor/sqlite_experimental_flamegraph_table.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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/sqlite_raw_table.cc b/src/trace_processor/sqlite_raw_table.cc
deleted file mode 100644
index 3177a962..0000000
--- a/src/trace_processor/sqlite_raw_table.cc
+++ /dev/null
@@ -1,385 +0,0 @@
-/*
- * 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/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/sqlite/sqlite_utils.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"
-#include "protos/perfetto/trace/ftrace/filemap.pbzero.h"
-#include "protos/perfetto/trace/ftrace/ftrace.pbzero.h"
-#include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
-#include "protos/perfetto/trace/ftrace/sched.pbzero.h"
-#include "protos/perfetto/trace/ftrace/workqueue.pbzero.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-namespace {
-std::tuple<uint32_t, uint32_t> ParseKernelReleaseVersion(
-    base::StringView system_release) {
-  size_t first_dot_pos = system_release.find(".");
-  size_t second_dot_pos = system_release.find(".", first_dot_pos + 1);
-  auto major_version = base::StringToUInt32(
-      system_release.substr(0, first_dot_pos).ToStdString());
-  auto minor_version = base::StringToUInt32(
-      system_release
-          .substr(first_dot_pos + 1, second_dot_pos - (first_dot_pos + 1))
-          .ToStdString());
-  return std::make_tuple(major_version.value(), minor_version.value());
-}
-}  // namespace
-
-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<SqliteRawTable*>(sqlite3_user_data(ctx));
-    thiz->ToSystrace(ctx, argc, argv);
-  };
-  sqlite3_create_function(db, "to_ftrace", 1,
-                          SQLITE_UTF8 | SQLITE_DETERMINISTIC, this, fn, nullptr,
-                          nullptr);
-}
-
-SqliteRawTable::~SqliteRawTable() = default;
-
-void SqliteRawTable::RegisterTable(sqlite3* db, const TraceStorage* storage) {
-  SqliteTable::Register<SqliteRawTable>(db, storage, "raw");
-}
-
-bool SqliteRawTable::ParseGfpFlags(Variadic value, base::StringWriter* writer) {
-  const auto& metadata_table = storage_->metadata_table();
-
-  auto opt_name_idx = metadata_table.name().IndexOf(
-      metadata::kNames[metadata::KeyIDs::system_name]);
-  auto opt_release_idx = metadata_table.name().IndexOf(
-      metadata::kNames[metadata::KeyIDs::system_release]);
-  if (!opt_name_idx || !opt_release_idx)
-    return false;
-
-  StringId name = metadata_table.str_value()[*opt_name_idx];
-  base::StringView system_name = storage_->GetString(name);
-  if (system_name != "Linux")
-    return false;
-
-  StringId release = metadata_table.str_value()[*opt_release_idx];
-  base::StringView system_release = storage_->GetString(release);
-  auto version = ParseKernelReleaseVersion(system_release);
-
-  WriteGfpFlag(value.uint_value, version, writer);
-  return true;
-}
-
-void SqliteRawTable::FormatSystraceArgs(NullTermStringView event_name,
-                                        ArgSetId arg_set_id,
-                                        base::StringWriter* writer) {
-  const auto& set_ids = storage_->arg_table().arg_set_id();
-
-  // 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)});
-
-  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) {
-      case Variadic::kInt:
-        writer->AppendInt(value.int_value);
-        break;
-      case Variadic::kUint:
-        writer->AppendUnsignedInt(value.uint_value);
-        break;
-      case Variadic::kString: {
-        const auto& str = storage_->GetString(value.string_value);
-        writer->AppendString(str.c_str(), str.size());
-        break;
-      }
-      case Variadic::kReal:
-        writer->AppendDouble(value.real_value);
-        break;
-      case Variadic::kPointer:
-        writer->AppendUnsignedInt(value.pointer_value);
-        break;
-      case Variadic::kBool:
-        writer->AppendBool(value.bool_value);
-        break;
-      case Variadic::kJson: {
-        const auto& str = storage_->GetString(value.json_value);
-        writer->AppendString(str.c_str(), str.size());
-        break;
-      }
-    }
-  };
-  auto write_value_at_index = [this, start_row](uint32_t arg_idx,
-                                                ValueWriter value_fn) {
-    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_->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(value, writer))
-      return;
-    value_fn(value);
-  };
-
-  if (event_name == "sched_switch") {
-    using SS = protos::pbzero::SchedSwitchFtraceEvent;
-    write_arg(SS::kPrevCommFieldNumber - 1, write_value);
-    write_arg(SS::kPrevPidFieldNumber - 1, write_value);
-    write_arg(SS::kPrevPrioFieldNumber - 1, write_value);
-    write_arg(SS::kPrevStateFieldNumber - 1, [writer](const Variadic& value) {
-      PERFETTO_DCHECK(value.type == Variadic::Type::kInt);
-      auto state = static_cast<uint16_t>(value.int_value);
-      writer->AppendString(ftrace_utils::TaskState(state).ToString('|').data());
-    });
-    writer->AppendLiteral(" ==>");
-    write_arg(SS::kNextCommFieldNumber - 1, write_value);
-    write_arg(SS::kNextPidFieldNumber - 1, write_value);
-    write_arg(SS::kNextPrioFieldNumber - 1, write_value);
-    return;
-  } else if (event_name == "sched_wakeup") {
-    using SW = protos::pbzero::SchedWakeupFtraceEvent;
-    write_arg(SW::kCommFieldNumber - 1, write_value);
-    write_arg(SW::kPidFieldNumber - 1, write_value);
-    write_arg(SW::kPrioFieldNumber - 1, write_value);
-    write_arg(SW::kTargetCpuFieldNumber - 1, [writer](const Variadic& value) {
-      PERFETTO_DCHECK(value.type == Variadic::Type::kInt);
-      writer->AppendPaddedInt<'0', 3>(value.int_value);
-    });
-    return;
-  } else if (event_name == "clock_set_rate") {
-    // TODO(lalitm): this is a big hack but the best way to do this now.
-    // Doing this requires overhauling how we deal with args by pushing them all
-    // to an array and then reading back from that array.
-
-    // We use the string "todo" as the name to stay consistent with old
-    // trace_to_text print code.
-    writer->AppendString(" todo");
-    write_arg(0 /* state */, write_value);
-    write_arg(1 /* cpu_id */, write_value);
-    return;
-  } else if (event_name == "clk_set_rate") {
-    using CSR = protos::pbzero::ClkSetRateFtraceEvent;
-    writer->AppendLiteral(" ");
-    write_value_at_index(CSR::kNameFieldNumber - 1, write_value);
-    writer->AppendLiteral(" ");
-    write_value_at_index(CSR::kRateFieldNumber - 1, write_value);
-    return;
-  } else if (event_name == "binder_transaction") {
-    using BT = protos::pbzero::BinderTransactionFtraceEvent;
-    writer->AppendString(" transaction=");
-    write_value_at_index(BT::kDebugIdFieldNumber - 1, write_value);
-    writer->AppendString(" dest_node=");
-    write_value_at_index(BT::kTargetNodeFieldNumber - 1, write_value);
-    writer->AppendString(" dest_proc=");
-    write_value_at_index(BT::kToProcFieldNumber - 1, write_value);
-    writer->AppendString(" dest_thread=");
-    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);
-                         });
-    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);
-                         });
-    return;
-  } else if (event_name == "binder_transaction_alloc_buf") {
-    using BTAB = protos::pbzero::BinderTransactionAllocBufFtraceEvent;
-    writer->AppendString(" transaction=");
-    write_value_at_index(BTAB::kDebugIdFieldNumber - 1, write_value);
-    write_arg(BTAB::kDataSizeFieldNumber - 1, write_value);
-    write_arg(BTAB::kOffsetsSizeFieldNumber - 1, write_value);
-    return;
-  } else if (event_name == "binder_transaction_received") {
-    using BTR = protos::pbzero::BinderTransactionReceivedFtraceEvent;
-    writer->AppendString(" transaction=");
-    write_value_at_index(BTR::kDebugIdFieldNumber - 1, write_value);
-    return;
-  } else if (event_name == "mm_filemap_add_to_page_cache") {
-    using MFA = protos::pbzero::MmFilemapAddToPageCacheFtraceEvent;
-    writer->AppendString(" dev ");
-    write_value_at_index(MFA::kSDevFieldNumber - 1,
-                         [writer](const Variadic& value) {
-                           PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
-                           writer->AppendUnsignedInt(value.uint_value >> 20);
-                         });
-    writer->AppendString(":");
-    write_value_at_index(
-        MFA::kSDevFieldNumber - 1, [writer](const Variadic& value) {
-          PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
-          writer->AppendUnsignedInt(value.uint_value & ((1 << 20) - 1));
-        });
-    writer->AppendString(" ino ");
-    write_value_at_index(MFA::kIInoFieldNumber - 1,
-                         [writer](const Variadic& value) {
-                           PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
-                           writer->AppendHexInt(value.uint_value);
-                         });
-    writer->AppendString(" page=0000000000000000");
-    writer->AppendString(" pfn=");
-    write_value_at_index(MFA::kPfnFieldNumber - 1, write_value);
-    writer->AppendString(" ofs=");
-    write_value_at_index(MFA::kIndexFieldNumber - 1,
-                         [writer](const Variadic& value) {
-                           PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
-                           writer->AppendUnsignedInt(value.uint_value << 12);
-                         });
-    return;
-  } 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 = 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'
-                              ? str.size() - 1
-                              : str.size();
-    writer->AppendChar(' ');
-    writer->AppendString(str.c_str(), chars_to_print);
-    return;
-  } else if (event_name == "sched_blocked_reason") {
-    using SBR = protos::pbzero::SchedBlockedReasonFtraceEvent;
-    write_arg(SBR::kPidFieldNumber - 1, write_value);
-    write_arg(SBR::kIoWaitFieldNumber - 1, write_value);
-    write_arg(SBR::kCallerFieldNumber - 1, [writer](const Variadic& value) {
-      PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
-      writer->AppendHexInt(value.uint_value);
-    });
-    return;
-  } else if (event_name == "workqueue_activate_work") {
-    using WAW = protos::pbzero::WorkqueueActivateWorkFtraceEvent;
-    writer->AppendString(" work struct ");
-    write_value_at_index(WAW::kWorkFieldNumber - 1,
-                         [writer](const Variadic& value) {
-                           PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
-                           writer->AppendHexInt(value.uint_value);
-                         });
-    return;
-  } else if (event_name == "workqueue_execute_start") {
-    using WES = protos::pbzero::WorkqueueExecuteStartFtraceEvent;
-    writer->AppendString(" work struct ");
-    write_value_at_index(WES::kWorkFieldNumber - 1,
-                         [writer](const Variadic& value) {
-                           PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
-                           writer->AppendHexInt(value.uint_value);
-                         });
-    writer->AppendString(": function ");
-    write_value_at_index(WES::kFunctionFieldNumber - 1,
-                         [writer](const Variadic& value) {
-                           PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
-                           writer->AppendHexInt(value.uint_value);
-                         });
-    return;
-  } else if (event_name == "workqueue_execute_end") {
-    using WE = protos::pbzero::WorkqueueExecuteEndFtraceEvent;
-    writer->AppendString(" work struct ");
-    write_value_at_index(WE::kWorkFieldNumber - 1,
-                         [writer](const Variadic& value) {
-                           PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
-                           writer->AppendHexInt(value.uint_value);
-                         });
-    return;
-  } else if (event_name == "workqueue_queue_work") {
-    using WQW = protos::pbzero::WorkqueueQueueWorkFtraceEvent;
-    writer->AppendString(" work struct=");
-    write_value_at_index(WQW::kWorkFieldNumber - 1,
-                         [writer](const Variadic& value) {
-                           PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
-                           writer->AppendHexInt(value.uint_value);
-                         });
-    write_arg(WQW::kFunctionFieldNumber - 1, [writer](const Variadic& value) {
-      PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
-      writer->AppendHexInt(value.uint_value);
-    });
-    write_arg(WQW::kWorkqueueFieldNumber - 1, [writer](const Variadic& value) {
-      PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
-      writer->AppendHexInt(value.uint_value);
-    });
-    write_value_at_index(WQW::kReqCpuFieldNumber - 1, write_value);
-    write_value_at_index(WQW::kCpuFieldNumber - 1, write_value);
-    return;
-  }
-
-  for (auto it = rm.IterateRows(); it; it.Next()) {
-    write_arg(it.index(), write_value);
-  }
-}
-
-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_table();
-
-  UniqueTid utid = raw_evts.utid()[row];
-  uint32_t tgid = 0;
-  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(storage_->thread_table().name()[utid]);
-
-  char line[4096];
-  base::StringWriter writer(line, sizeof(line));
-
-  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()[row]);
-  writer.AppendChar(' ');
-  if (event_name == "print") {
-    writer.AppendString("tracing_mark_write");
-  } else {
-    writer.AppendString(event_name.c_str(), event_name.size());
-  }
-  writer.AppendChar(':');
-
-  FormatSystraceArgs(event_name, raw_evts.arg_set_id()[row], &writer);
-  sqlite3_result_text(ctx, writer.CreateStringCopy(), -1, free);
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/sqlite_raw_table.h b/src/trace_processor/sqlite_raw_table.h
deleted file mode 100644
index 99ebff2..0000000
--- a/src/trace_processor/sqlite_raw_table.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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_SQLITE_RAW_TABLE_H_
-#define SRC_TRACE_PROCESSOR_SQLITE_RAW_TABLE_H_
-
-#include "perfetto/base/logging.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 SqliteRawTable : public DbSqliteTable {
- public:
-  SqliteRawTable(sqlite3*, const TraceStorage*);
-  virtual ~SqliteRawTable();
-
-  static void RegisterTable(sqlite3* db, const TraceStorage* storage);
-
- private:
-  void FormatSystraceArgs(NullTermStringView event_name,
-                          ArgSetId arg_set_id,
-                          base::StringWriter* writer);
-  void ToSystrace(sqlite3_context* ctx, int argc, sqlite3_value** argv);
-  bool ParseGfpFlags(Variadic value, base::StringWriter* writer);
-
-  const TraceStorage* const storage_;
-};
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_SQLITE_RAW_TABLE_H_
diff --git a/src/trace_processor/storage/BUILD.gn b/src/trace_processor/storage/BUILD.gn
new file mode 100644
index 0000000..47e1195
--- /dev/null
+++ b/src/trace_processor/storage/BUILD.gn
@@ -0,0 +1,32 @@
+# Copyright (C) 20 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")
+
+source_set("storage") {
+  sources = [
+    "metadata.h",
+    "stats.h",
+    "trace_storage.cc",
+    "trace_storage.h",
+  ]
+  deps = [
+    "../../../gn:default_deps",
+    "../../../include/perfetto/ext/base",
+    "../../../include/perfetto/trace_processor",
+    "../containers",
+    "../tables",
+    "../types",
+  ]
+}
diff --git a/src/trace_processor/metadata.h b/src/trace_processor/storage/metadata.h
similarity index 94%
rename from src/trace_processor/metadata.h
rename to src/trace_processor/storage/metadata.h
index 2546cec..f1b6c4b 100644
--- a/src/trace_processor/metadata.h
+++ b/src/trace_processor/storage/metadata.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_METADATA_H_
-#define SRC_TRACE_PROCESSOR_METADATA_H_
+#ifndef SRC_TRACE_PROCESSOR_STORAGE_METADATA_H_
+#define SRC_TRACE_PROCESSOR_STORAGE_METADATA_H_
 
 #include <stddef.h>
 
@@ -38,7 +38,6 @@
   F(benchmark_story_run_index,         KeyType::kSingle,  Variadic::kInt),    \
   F(benchmark_story_run_time_us,       KeyType::kSingle,  Variadic::kInt),    \
   F(benchmark_story_tags,              KeyType::kMulti,   Variadic::kString), \
-  F(android_packages_list,             KeyType::kMulti,   Variadic::kInt),    \
   F(statsd_triggering_subscription_id, KeyType::kSingle,  Variadic::kInt),    \
   F(trace_uuid,                        KeyType::kSingle,  Variadic::kString), \
   F(system_name,                       KeyType::kSingle,  Variadic::kString), \
@@ -46,7 +45,8 @@
   F(system_release,                    KeyType::kSingle,  Variadic::kString), \
   F(system_machine,                    KeyType::kSingle,  Variadic::kString), \
   F(android_build_fingerprint,         KeyType::kSingle,  Variadic::kString), \
-  F(trace_size_bytes,                  KeyType::kSingle,  Variadic::kInt)
+  F(trace_size_bytes,                  KeyType::kSingle,  Variadic::kInt), \
+  F(all_data_source_started_ns,        KeyType::kSingle,  Variadic::kInt)
 // clang-format on
 
 // Compile time list of metadata items.
@@ -97,4 +97,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_METADATA_H_
+#endif  // SRC_TRACE_PROCESSOR_STORAGE_METADATA_H_
diff --git a/src/trace_processor/stats.h b/src/trace_processor/storage/stats.h
similarity index 88%
rename from src/trace_processor/stats.h
rename to src/trace_processor/storage/stats.h
index 8d52968..1e6557e 100644
--- a/src/trace_processor/stats.h
+++ b/src/trace_processor/storage/stats.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_STATS_H_
-#define SRC_TRACE_PROCESSOR_STATS_H_
+#ifndef SRC_TRACE_PROCESSOR_STORAGE_STATS_H_
+#define SRC_TRACE_PROCESSOR_STORAGE_STATS_H_
 
 #include <stddef.h>
 
@@ -108,16 +108,22 @@
   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),    \
   F(heap_graph_non_finalized_graph,           kSingle,  kError,    kTrace),    \
   F(heap_graph_malformed_packet,              kIndexed, kError,    kTrace),    \
-  F(heap_graph_missing_packet,                kIndexed, kDataLoss, kTrace),    \
+  F(heap_graph_missing_packet,                kIndexed, kError,    kTrace),    \
+  F(heap_graph_location_parse_error,          kSingle,  kError,    kTrace),    \
   F(heapprofd_buffer_corrupted,               kIndexed, kError,    kTrace),    \
+  F(heapprofd_hit_guardrail,                  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(heapprofd_non_finalized_profile,          kSingle,  kError,    kTrace),    \
   F(metatrace_overruns,                       kSingle,  kError,    kTrace),    \
   F(packages_list_has_parse_errors,           kSingle,  kError,    kTrace),    \
   F(packages_list_has_read_errors,            kSingle,  kError,    kTrace),    \
@@ -126,7 +132,13 @@
   F(sched_waking_out_of_order,                kSingle,  kError,    kAnalysis), \
   F(compact_sched_switch_skipped,             kSingle,  kInfo,     kAnalysis), \
   F(compact_sched_waking_skipped,             kSingle,  kInfo,     kAnalysis), \
-  F(empty_chrome_metadata,                    kSingle,  kError,    kTrace)
+  F(empty_chrome_metadata,                    kSingle,  kError,    kTrace),    \
+  F(perf_cpu_lost_records,                    kIndexed, kDataLoss, kTrace),    \
+  F(ninja_parse_errors,                       kSingle,  kError,    kTrace),    \
+  F(perf_samples_skipped,                     kSingle,  kInfo,     kTrace),    \
+  F(perf_samples_skipped_dataloss,            kSingle,  kDataLoss, kTrace),    \
+  F(thread_time_in_state_out_of_order,        kSingle,  kError,    kAnalysis), \
+  F(thread_time_in_state_unknown_cpu_freq,    kSingle,  kError,    kAnalysis)
 // clang-format on
 
 enum Type {
@@ -137,7 +149,9 @@
 enum Severity {
   kInfo,      // Diagnostic counters
   kDataLoss,  // Correct operation that still resulted in data loss
-  kError      // If any kError counter is > 0 the UI will raise an error
+  kError      // If any kError counter is > 0 trace_processor_shell will
+              // raise an error. This is *not* surfaced in the web UI.
+              // TODO(b/148587181): Surface these errors in the UI.
 };
 
 enum Source {
@@ -177,4 +191,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_STATS_H_
+#endif  // SRC_TRACE_PROCESSOR_STORAGE_STATS_H_
diff --git a/src/trace_processor/trace_storage.cc b/src/trace_processor/storage/trace_storage.cc
similarity index 77%
rename from src/trace_processor/trace_storage.cc
rename to src/trace_processor/storage/trace_storage.cc
index cbd01b5..b66ce82 100644
--- a/src/trace_processor/trace_storage.cc
+++ b/src/trace_processor/storage/trace_storage.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/storage/trace_storage.h"
 
 #include <string.h>
 #include <algorithm>
@@ -27,33 +27,27 @@
 
 namespace {
 
-template <typename T>
-void MaybeUpdateMinMax(T begin_it,
-                       T end_it,
-                       int64_t* min_value,
-                       int64_t* max_value) {
-  if (begin_it == end_it) {
-    return;
-  }
-  std::pair<T, T> minmax = std::minmax_element(begin_it, end_it);
-  *min_value = std::min(*min_value, *minmax.first);
-  *max_value = std::max(*max_value, *minmax.second);
-}
-
-void DbTableMaybeUpdateMinMax(const TypedColumn<int64_t>& column,
+void DbTableMaybeUpdateMinMax(const TypedColumn<int64_t>& ts_col,
                               int64_t* min_value,
-                              int64_t* max_value) {
-  if (column.row_map().empty())
+                              int64_t* max_value,
+                              const TypedColumn<int64_t>* dur_col = nullptr) {
+  if (ts_col.row_map().empty())
     return;
 
-  SqlValue col_min = *column.Min();
-  SqlValue col_max = *column.Max();
+  int64_t col_min = ts_col.Min()->AsLong();
+  int64_t col_max = ts_col.Max()->AsLong();
 
-  PERFETTO_DCHECK(col_min.type == SqlValue::Type::kLong);
-  PERFETTO_DCHECK(col_max.type == SqlValue::Type::kLong);
+  if (dur_col) {
+    PERFETTO_CHECK(ts_col.IsSorted());
+    PERFETTO_CHECK(dur_col->row_map().size() == ts_col.row_map().size());
+    for (uint32_t i = 0; i < dur_col->row_map().size(); i++) {
+      col_max =
+          std::max(ts_col.Get(i).AsLong() + dur_col->Get(i).AsLong(), col_max);
+    }
+  }
 
-  *min_value = std::min(*min_value, col_min.long_value);
-  *max_value = std::max(*max_value, col_max.long_value);
+  *min_value = std::min(*min_value, col_min);
+  *max_value = std::max(*max_value, col_max);
 }
 
 std::vector<NullTermStringView> CreateRefTypeStringMap() {
@@ -78,7 +72,10 @@
 }
 
 TraceStorage::TraceStorage(const Config&) {
-  // Upid/utid 0 is reserved for idle processes/threads.
+  // Reserve utid/upid 0. These are special as embedders (e.g. Perfetto UI)
+  // exclude them by filtering them out. If the parsed trace contains ftrace
+  // data, ProcessTracker::SetPidZeroIgnoredForIdleProcess will create a mapping
+  // to these rows for tid/pid 0.
   tables::ThreadTable::Row thread_row;
   thread_row.tid = 0;
   thread_table_.Insert(thread_row);
@@ -137,16 +134,19 @@
 std::pair<int64_t, int64_t> TraceStorage::GetTraceTimestampBoundsNs() const {
   int64_t start_ns = std::numeric_limits<int64_t>::max();
   int64_t end_ns = std::numeric_limits<int64_t>::min();
-  MaybeUpdateMinMax(slices_.start_ns().begin(), slices_.start_ns().end(),
-                    &start_ns, &end_ns);
 
   DbTableMaybeUpdateMinMax(raw_table_.ts(), &start_ns, &end_ns);
+  DbTableMaybeUpdateMinMax(sched_slice_table_.ts(), &start_ns, &end_ns,
+                           &sched_slice_table_.dur());
   DbTableMaybeUpdateMinMax(counter_table_.ts(), &start_ns, &end_ns);
-  DbTableMaybeUpdateMinMax(slice_table_.ts(), &start_ns, &end_ns);
+  DbTableMaybeUpdateMinMax(slice_table_.ts(), &start_ns, &end_ns,
+                           &slice_table_.dur());
   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/storage/trace_storage.h
similarity index 88%
rename from src/trace_processor/trace_storage.h
rename to src/trace_processor/storage/trace_storage.h
index 67c6358..ab1aae9 100644
--- a/src/trace_processor/trace_storage.h
+++ b/src/trace_processor/storage/trace_storage.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_TRACE_STORAGE_H_
-#define SRC_TRACE_PROCESSOR_TRACE_STORAGE_H_
+#ifndef SRC_TRACE_PROCESSOR_STORAGE_TRACE_STORAGE_H_
+#define SRC_TRACE_PROCESSOR_STORAGE_TRACE_STORAGE_H_
 
 #include <array>
 #include <deque>
@@ -33,9 +33,8 @@
 #include "perfetto/ext/base/utils.h"
 #include "perfetto/trace_processor/basic_types.h"
 #include "src/trace_processor/containers/string_pool.h"
-#include "src/trace_processor/ftrace_utils.h"
-#include "src/trace_processor/metadata.h"
-#include "src/trace_processor/stats.h"
+#include "src/trace_processor/storage/metadata.h"
+#include "src/trace_processor/storage/stats.h"
 #include "src/trace_processor/tables/android_tables.h"
 #include "src/trace_processor/tables/counter_tables.h"
 #include "src/trace_processor/tables/metadata_tables.h"
@@ -71,6 +70,8 @@
 
 using InstantId = tables::InstantTable::Id;
 
+using SchedId = tables::SchedSliceTable::Id;
+
 using MappingId = tables::StackProfileMappingTable::Id;
 
 using FrameId = tables::StackProfileFrameTable::Id;
@@ -83,6 +84,8 @@
 
 using RawId = tables::RawTable::Id;
 
+using FlamegraphId = tables::ExperimentalFlamegraphNodesTable::Id;
+
 using VulkanAllocId = tables::VulkanMemoryAllocationsTable::Id;
 
 // TODO(lalitm): this is a temporary hack while migrating the counters table and
@@ -113,69 +116,6 @@
 
   virtual ~TraceStorage();
 
-  class Slices {
-   public:
-    inline size_t AddSlice(uint32_t cpu,
-                           int64_t start_ns,
-                           int64_t duration_ns,
-                           UniqueTid utid,
-                           ftrace_utils::TaskState end_state,
-                           int32_t priority) {
-      cpus_.emplace_back(cpu);
-      start_ns_.emplace_back(start_ns);
-      durations_.emplace_back(duration_ns);
-      utids_.emplace_back(utid);
-      end_states_.emplace_back(end_state);
-      priorities_.emplace_back(priority);
-
-      if (utid >= rows_for_utids_.size())
-        rows_for_utids_.resize(utid + 1);
-      rows_for_utids_[utid].emplace_back(slice_count() - 1);
-      return slice_count() - 1;
-    }
-
-    void set_duration(size_t index, int64_t duration_ns) {
-      durations_[index] = duration_ns;
-    }
-
-    void set_end_state(size_t index, ftrace_utils::TaskState end_state) {
-      end_states_[index] = end_state;
-    }
-
-    size_t slice_count() const { return start_ns_.size(); }
-
-    const std::deque<uint32_t>& cpus() const { return cpus_; }
-
-    const std::deque<int64_t>& start_ns() const { return start_ns_; }
-
-    const std::deque<int64_t>& durations() const { return durations_; }
-
-    const std::deque<UniqueTid>& utids() const { return utids_; }
-
-    const std::deque<ftrace_utils::TaskState>& end_state() const {
-      return end_states_;
-    }
-
-    const std::deque<int32_t>& priorities() const { return priorities_; }
-
-    const std::deque<std::vector<uint32_t>>& rows_for_utids() const {
-      return rows_for_utids_;
-    }
-
-   private:
-    // Each deque below has the same number of entries (the number of slices
-    // in the trace for the CPU).
-    std::deque<uint32_t> cpus_;
-    std::deque<int64_t> start_ns_;
-    std::deque<int64_t> durations_;
-    std::deque<UniqueTid> utids_;
-    std::deque<ftrace_utils::TaskState> end_states_;
-    std::deque<int32_t> priorities_;
-
-    // One row per utid.
-    std::deque<std::vector<uint32_t>> rows_for_utids_;
-  };
-
   class ThreadSlices {
    public:
     inline uint32_t AddThreadSlice(uint32_t slice_id,
@@ -484,8 +424,12 @@
     return &gpu_counter_track_table_;
   }
 
-  const Slices& slices() const { return slices_; }
-  Slices* mutable_slices() { return &slices_; }
+  const tables::SchedSliceTable& sched_slice_table() const {
+    return sched_slice_table_;
+  }
+  tables::SchedSliceTable* mutable_sched_slice_table() {
+    return &sched_slice_table_;
+  }
 
   const tables::SliceTable& slice_table() const { return slice_table_; }
   tables::SliceTable* mutable_slice_table() { return &slice_table_; }
@@ -564,6 +508,20 @@
     return &heap_profile_allocation_table_;
   }
 
+  const tables::PackageListTable& package_list_table() const {
+    return package_list_table_;
+  }
+  tables::PackageListTable* mutable_package_list_table() {
+    return &package_list_table_;
+  }
+
+  const tables::ProfilerSmapsTable& profiler_smaps_table() const {
+    return profiler_smaps_table_;
+  }
+  tables::ProfilerSmapsTable* mutable_profiler_smaps_table() {
+    return &profiler_smaps_table_;
+  }
+
   const tables::CpuProfileStackSampleTable& cpu_profile_stack_sample_table()
       const {
     return cpu_profile_stack_sample_table_;
@@ -583,6 +541,13 @@
   tables::HeapGraphObjectTable* mutable_heap_graph_object_table() {
     return &heap_graph_object_table_;
   }
+  const tables::HeapGraphClassTable& heap_graph_class_table() const {
+    return heap_graph_class_table_;
+  }
+
+  tables::HeapGraphClassTable* mutable_heap_graph_class_table() {
+    return &heap_graph_class_table_;
+  }
 
   const tables::HeapGraphReferenceTable& heap_graph_reference_table() const {
     return heap_graph_reference_table_;
@@ -607,6 +572,22 @@
     return &vulkan_memory_allocations_table_;
   }
 
+  const tables::GraphicsFrameSliceTable& graphics_frame_slice_table() const {
+    return graphics_frame_slice_table_;
+  }
+
+  tables::GraphicsFrameSliceTable* mutable_graphics_frame_slice_table() {
+    return &graphics_frame_slice_table_;
+  }
+
+  const tables::GraphicsFrameStatsTable& graphics_frame_stats_table() const {
+    return graphics_frame_stats_table_;
+  }
+
+  tables::GraphicsFrameStatsTable* mutable_graphics_frame_stats_table() {
+    return &graphics_frame_stats_table_;
+  }
+
   const StringPool& string_pool() const { return string_pool_; }
   StringPool* mutable_string_pool() { return &string_pool_; }
 
@@ -665,18 +646,22 @@
       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];
+      case Variadic::Type::kString: {
+        auto opt_value = arg_table_.string_value()[row];
+        v.string_value = opt_value ? *opt_value : kNullStringId;
         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];
+      case Variadic::Type::kJson: {
+        auto opt_value = arg_table_.string_value()[row];
+        v.json_value = opt_value ? *opt_value : kNullStringId;
         break;
+      }
     }
     return v;
   }
@@ -685,15 +670,6 @@
     return variadic_type_ids_[type];
   }
 
- private:
-  using StringHash = uint64_t;
-
-  TraceStorage(const TraceStorage&) = delete;
-  TraceStorage& operator=(const TraceStorage&) = delete;
-
-  TraceStorage(TraceStorage&&) = delete;
-  TraceStorage& operator=(TraceStorage&&) = delete;
-
   base::Optional<Variadic::Type> GetVariadicTypeForId(StringId id) const {
     auto it =
         std::find(variadic_type_ids_.begin(), variadic_type_ids_.end(), id);
@@ -704,6 +680,15 @@
     return static_cast<Variadic::Type>(idx);
   }
 
+ private:
+  using StringHash = uint64_t;
+
+  TraceStorage(const TraceStorage&) = delete;
+  TraceStorage& operator=(const TraceStorage&) = delete;
+
+  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<MappingId>> stack_profile_mapping_index_;
@@ -744,9 +729,6 @@
   tables::GpuCounterTrackTable gpu_counter_track_table_{&string_pool_,
                                                         &counter_track_table_};
 
-  // One entry for each CPU in the trace.
-  Slices slices_;
-
   // Args for all other tables.
   tables::ArgTable arg_table_{&string_pool_, nullptr};
 
@@ -757,6 +739,9 @@
   // Slices coming from userspace events (e.g. Chromium TRACE_EVENT macros).
   tables::SliceTable slice_table_{&string_pool_, nullptr};
 
+  // Slices from CPU scheduling data.
+  tables::SchedSliceTable sched_slice_table_{&string_pool_, nullptr};
+
   // Additional attributes for threads slices (sub-type of NestableSlices).
   ThreadSlices thread_slices_;
 
@@ -796,16 +781,24 @@
       &string_pool_, nullptr};
   tables::CpuProfileStackSampleTable cpu_profile_stack_sample_table_{
       &string_pool_, nullptr};
+  tables::PackageListTable package_list_table_{&string_pool_, nullptr};
+  tables::ProfilerSmapsTable profiler_smaps_table_{&string_pool_, nullptr};
 
   // Symbol tables (mappings from frames to symbol names)
   tables::SymbolTable symbol_table_{&string_pool_, nullptr};
   tables::HeapGraphObjectTable heap_graph_object_table_{&string_pool_, nullptr};
+  tables::HeapGraphClassTable heap_graph_class_table_{&string_pool_, nullptr};
   tables::HeapGraphReferenceTable heap_graph_reference_table_{&string_pool_,
                                                               nullptr};
 
   tables::VulkanMemoryAllocationsTable vulkan_memory_allocations_table_{
       &string_pool_, nullptr};
 
+  tables::GraphicsFrameSliceTable graphics_frame_slice_table_{&string_pool_,
+                                                              &slice_table_};
+  tables::GraphicsFrameStatsTable graphics_frame_stats_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_;
@@ -817,8 +810,8 @@
 namespace std {
 
 template <>
-struct hash<::perfetto::trace_processor::TrackId> {
-  using argument_type = ::perfetto::trace_processor::TrackId;
+struct hash<::perfetto::trace_processor::BaseId> {
+  using argument_type = ::perfetto::trace_processor::BaseId;
   using result_type = size_t;
 
   result_type operator()(const argument_type& r) const {
@@ -827,6 +820,19 @@
 };
 
 template <>
+struct hash<::perfetto::trace_processor::TrackId>
+    : hash<::perfetto::trace_processor::BaseId> {};
+template <>
+struct hash<::perfetto::trace_processor::MappingId>
+    : hash<::perfetto::trace_processor::BaseId> {};
+template <>
+struct hash<::perfetto::trace_processor::CallsiteId>
+    : hash<::perfetto::trace_processor::BaseId> {};
+template <>
+struct hash<::perfetto::trace_processor::FrameId>
+    : hash<::perfetto::trace_processor::BaseId> {};
+
+template <>
 struct hash<::perfetto::trace_processor::tables::StackProfileFrameTable::Row> {
   using argument_type =
       ::perfetto::trace_processor::tables::StackProfileFrameTable::Row;
@@ -834,7 +840,9 @@
 
   result_type operator()(const argument_type& r) const {
     return std::hash<::perfetto::trace_processor::StringId>{}(r.name) ^
-           std::hash<int64_t>{}(r.mapping) ^ std::hash<int64_t>{}(r.rel_pc);
+           std::hash<::perfetto::base::Optional<
+               ::perfetto::trace_processor::MappingId>>{}(r.mapping) ^
+           std::hash<int64_t>{}(r.rel_pc);
   }
 };
 
@@ -846,8 +854,10 @@
   using result_type = size_t;
 
   result_type operator()(const argument_type& r) const {
-    return std::hash<int64_t>{}(r.depth) ^ std::hash<int64_t>{}(r.parent_id) ^
-           std::hash<int64_t>{}(r.frame_id);
+    return std::hash<int64_t>{}(r.depth) ^
+           std::hash<::perfetto::base::Optional<
+               ::perfetto::trace_processor::CallsiteId>>{}(r.parent_id) ^
+           std::hash<::perfetto::trace_processor::FrameId>{}(r.frame_id);
   }
 };
 
@@ -870,4 +880,4 @@
 
 }  // namespace std
 
-#endif  // SRC_TRACE_PROCESSOR_TRACE_STORAGE_H_
+#endif  // SRC_TRACE_PROCESSOR_STORAGE_TRACE_STORAGE_H_
diff --git a/src/trace_processor/storage_columns.cc b/src/trace_processor/storage_columns.cc
deleted file mode 100644
index 3b47c0a..0000000
--- a/src/trace_processor/storage_columns.cc
+++ /dev/null
@@ -1,40 +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/storage_columns.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-StorageColumn::StorageColumn(std::string col_name, bool hidden)
-    : col_name_(col_name), hidden_(hidden) {}
-StorageColumn::~StorageColumn() = default;
-
-StringPoolAccessor::StringPoolAccessor(const std::deque<StringId>* deque,
-                                       const StringPool* string_pool)
-    : deque_(deque), string_pool_(string_pool) {}
-StringPoolAccessor::~StringPoolAccessor() = default;
-
-TsEndAccessor::TsEndAccessor(const std::deque<int64_t>* ts,
-                             const std::deque<int64_t>* dur)
-    : ts_(ts), dur_(dur) {}
-TsEndAccessor::~TsEndAccessor() = default;
-
-RowAccessor::RowAccessor() = default;
-RowAccessor::~RowAccessor() = default;
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/storage_columns.h b/src/trace_processor/storage_columns.h
deleted file mode 100644
index 33ead88..0000000
--- a/src/trace_processor/storage_columns.h
+++ /dev/null
@@ -1,432 +0,0 @@
-/*
- * 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_STORAGE_COLUMNS_H_
-#define SRC_TRACE_PROCESSOR_STORAGE_COLUMNS_H_
-
-#include <deque>
-#include <limits>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "src/trace_processor/filtered_row_index.h"
-#include "src/trace_processor/sqlite/sqlite_utils.h"
-#include "src/trace_processor/trace_storage.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-// A column of data backed by data storage.
-class StorageColumn {
- public:
-  struct Bounds {
-    uint32_t min_idx = 0;
-    uint32_t max_idx = std::numeric_limits<uint32_t>::max();
-    bool consumed = false;
-  };
-  using Predicate = std::function<bool(uint32_t)>;
-  using Comparator = std::function<int(uint32_t, uint32_t)>;
-
-  StorageColumn(std::string col_name, bool hidden);
-  virtual ~StorageColumn();
-
-  // Implements StorageCursor::ColumnReporter.
-  virtual void ReportResult(sqlite3_context*, uint32_t row) const = 0;
-
-  // Given a SQLite operator and value for the comparision, returns a
-  // predicate which takes in a row index and returns whether the row should
-  // be returned.
-  virtual void Filter(int op, sqlite3_value*, FilteredRowIndex*) const = 0;
-
-  // Given a order by constraint for this column, returns a comparator
-  // function which compares data in this column at two indices.
-  virtual Comparator Sort(const QueryConstraints::OrderBy& ob) const = 0;
-
-  // Returns the type of this column.
-  virtual SqlValue::Type GetType() const = 0;
-
-  // Bounds a filter on this column between a minimum and maximum index.
-  // Generally this is only possible if the column is sorted.
-  virtual Bounds BoundFilter(int, sqlite3_value*) const { return Bounds{}; }
-
-  // Returns whether this column is ordered.
-  virtual bool HasOrdering() const { return false; }
-
-  const std::string& name() const { return col_name_; }
-  bool hidden() const { return hidden_; }
-
- private:
-  std::string col_name_;
-  bool hidden_ = false;
-};
-
-// The implementation of StorageColumn for Strings.
-// The actual retrieval of the numerics from the data types is left to the
-// Acessor trait (see below for definition).
-template <typename Accessor /* <NullTermStringView> */>
-class StringColumn final : public StorageColumn {
- public:
-  StringColumn(std::string col_name, Accessor accessor, bool hidden = false)
-      : StorageColumn(col_name, hidden), accessor_(accessor) {}
-
-  void ReportResult(sqlite3_context* ctx, uint32_t row) const override {
-    NullTermStringView str = accessor_.Get(row);
-    if (str.c_str() == nullptr) {
-      sqlite3_result_null(ctx);
-    } else {
-      sqlite3_result_text(ctx, str.c_str(), -1, sqlite_utils::kSqliteStatic);
-    }
-  }
-
-  Bounds BoundFilter(int, sqlite3_value*) const override {
-    Bounds bounds;
-    bounds.max_idx = static_cast<uint32_t>(accessor_.Size());
-    return bounds;
-  }
-
-  void Filter(int, sqlite3_value*, FilteredRowIndex*) const override {}
-
-  Comparator Sort(const QueryConstraints::OrderBy& ob) const override {
-    if (ob.desc) {
-      return [this](uint32_t f, uint32_t s) {
-        NullTermStringView a = accessor_.Get(f);
-        NullTermStringView b = accessor_.Get(s);
-        return sqlite_utils::CompareValuesDesc(a, b);
-      };
-    }
-    return [this](uint32_t f, uint32_t s) {
-      NullTermStringView a = accessor_.Get(f);
-      NullTermStringView b = accessor_.Get(s);
-      return sqlite_utils::CompareValuesAsc(a, b);
-    };
-  }
-
-  SqlValue::Type GetType() const override { return SqlValue::Type::kString; }
-
-  bool HasOrdering() const override { return accessor_.HasOrdering(); }
-
- private:
-  Accessor accessor_;
-};
-
-// The implementation of StorageColumn for numeric data types.
-// The actual retrieval of the numerics from the data types is left to the
-// Acessor trait (see below for definition).
-template <typename Accessor,
-          typename sqlite_utils::is_numeric<typename Accessor::Type>* = nullptr>
-class NumericStorageColumn : public StorageColumn {
- public:
-  // The type of the column. This is one of uint32_t, int32_t, uint64_t etc.
-  using NumericType = typename Accessor::Type;
-
-  NumericStorageColumn(std::string col_name, bool hidden, Accessor accessor)
-      : StorageColumn(col_name, hidden), accessor_(accessor) {}
-  ~NumericStorageColumn() override = default;
-
-  void ReportResult(sqlite3_context* ctx, uint32_t row) const override {
-    sqlite_utils::ReportSqliteResult(ctx, accessor_.Get(row));
-  }
-
-  Bounds BoundFilter(int op, sqlite3_value* sqlite_val) const override {
-    Bounds bounds;
-    bounds.max_idx = accessor_.Size();
-
-    if (!accessor_.HasOrdering())
-      return bounds;
-
-    // Makes the below code much more readable.
-    using namespace sqlite_utils;
-
-    NumericType min = kTMin;
-    NumericType max = kTMax;
-    if (IsOpGe(op) || IsOpGt(op)) {
-      min = FindGtBound<NumericType>(IsOpGe(op), sqlite_val);
-    } else if (IsOpLe(op) || IsOpLt(op)) {
-      max = FindLtBound<NumericType>(IsOpLe(op), sqlite_val);
-    } else if (IsOpEq(op)) {
-      auto val = FindEqBound<NumericType>(sqlite_val);
-      min = val;
-      max = val;
-    }
-
-    if (min <= kTMin && max >= kTMax)
-      return bounds;
-
-    bounds.min_idx = accessor_.LowerBoundIndex(min);
-    bounds.max_idx = accessor_.UpperBoundIndex(max);
-    bounds.consumed = true;
-
-    return bounds;
-  }
-
-  void Filter(int op,
-              sqlite3_value* value,
-              FilteredRowIndex* index) const override {
-    auto type = sqlite3_value_type(value);
-
-    bool same_type = (kIsIntegralType && type == SQLITE_INTEGER) ||
-                     (kIsRealType && type == SQLITE_FLOAT);
-    if (sqlite_utils::IsOpEq(op) && same_type &&
-        accessor_.CanFindEqualIndices()) {
-      auto raw = sqlite_utils::ExtractSqliteValue<NumericType>(value);
-      index->IntersectRows(accessor_.EqualIndices(raw));
-      return;
-    }
-
-    if (kIsIntegralType && (type == SQLITE_INTEGER || type == SQLITE_NULL)) {
-      FilterWithCast<int64_t>(op, value, index);
-    } else if (type == SQLITE_INTEGER || type == SQLITE_FLOAT ||
-               type == SQLITE_NULL) {
-      FilterWithCast<double>(op, value, index);
-    } else {
-      PERFETTO_FATAL("Unexpected sqlite value to compare against");
-    }
-  }
-
-  Comparator Sort(const QueryConstraints::OrderBy& ob) const override {
-    if (ob.desc) {
-      return [this](uint32_t f, uint32_t s) {
-        return sqlite_utils::CompareValuesDesc(accessor_.Get(f),
-                                               accessor_.Get(s));
-      };
-    }
-    return [this](uint32_t f, uint32_t s) {
-      return sqlite_utils::CompareValuesAsc(accessor_.Get(f), accessor_.Get(s));
-    };
-  }
-
-  bool HasOrdering() const override { return accessor_.HasOrdering(); }
-
-  SqlValue::Type GetType() const override {
-    if (std::is_same<NumericType, uint8_t>::value ||
-        std::is_same<NumericType, uint32_t>::value ||
-        std::is_same<NumericType, int32_t>::value ||
-        std::is_same<NumericType, int64_t>::value) {
-      return SqlValue::Type::kLong;
-    } else if (std::is_same<NumericType, double>::value) {
-      return SqlValue::Type::kDouble;
-    }
-    PERFETTO_FATAL("Unexpected column type");
-  }
-
- private:
-  static constexpr bool kIsIntegralType = std::is_integral<NumericType>::value;
-  static constexpr bool kIsRealType =
-      std::is_floating_point<NumericType>::value;
-
-  NumericType kTMin = std::numeric_limits<NumericType>::lowest();
-  NumericType kTMax = std::numeric_limits<NumericType>::max();
-
-  // Filters the rows of this column by creating the predicate from the sqlite
-  // value using type |UpcastNumericType| and casting data from the column
-  // to also be this type.
-  // Note: We cast here to make numeric comparisions as accurate as possible.
-  // For example, suppose NumericType == uint32_t and the sqlite value has
-  // an integer. Then UpcastNumericType == int64_t because uint32_t can be
-  // upcast to an int64_t and it's the most generic type we can compare using.
-  // Alternatively if either the column or sqlite value is real, we will always
-  // cast to a double before comparing.
-  template <typename UpcastNumericType>
-  void FilterWithCast(int op,
-                      sqlite3_value* value,
-                      FilteredRowIndex* index) const {
-    auto predicate =
-        sqlite_utils::CreateNumericPredicate<UpcastNumericType>(op, value);
-    auto cast_predicate = [this,
-                           predicate](uint32_t row) PERFETTO_ALWAYS_INLINE {
-      return predicate(static_cast<UpcastNumericType>(accessor_.Get(row)));
-    };
-    index->FilterRows(cast_predicate);
-  }
-
-  Accessor accessor_;
-};
-
-// Defines an accessor for columns.
-// An accessor is a abstraction over the method to retrieve data in a column. As
-// there are many possible types of backing data (std::vector, std::deque,
-// creating on the flight etc.), this class hides this complexity behind an
-// interface to let the column implementation focus on actually interfacing
-// with SQLite and rest of trace processor.
-// This class exists as an interface for documentation purposes. There should
-// be no use of it apart from classes inheriting from it to ensure they comply
-// with the requirements of the interface.
-template <typename DataType>
-class Accessor {
- public:
-  using Type = DataType;
-
-  virtual ~Accessor() = default;
-
-  // Returns the number of elements in the backing storage.
-  virtual uint32_t Size() const = 0;
-
-  // Returns the element located at index |idx|.
-  virtual Type Get(uint32_t idx) const = 0;
-
-  // Returns whether the backing data source is ordered. |LowerBoundIndex| and
-  // |UpperBoundIndex| will be called only if HasOrdering() returns true.
-  virtual bool HasOrdering() const { return false; }
-
-  // Returns the index of the lower bound of the value.
-  virtual uint32_t LowerBoundIndex(Type) const { PERFETTO_CHECK(false); }
-
-  // Returns the index of the lower bound of the value.
-  virtual uint32_t UpperBoundIndex(Type) const { PERFETTO_CHECK(false); }
-
-  // Returns whether the backing data sources can efficiently provide the
-  // indices of elements equal to a given value. |EqualIndices| will be called
-  // only if |CanFindEqualIndices| returns true.
-  virtual bool CanFindEqualIndices() const { return false; }
-
-  // Returns the indices into the backing data source with value equal to
-  // |value|.
-  virtual std::vector<uint32_t> EqualIndices(Type) const {
-    PERFETTO_CHECK(false);
-  }
-};
-
-// An accessor implementation for string which uses a deque to store offsets
-// into a StringPool.
-class StringPoolAccessor : public Accessor<NullTermStringView> {
- public:
-  StringPoolAccessor(const std::deque<StringPool::Id>* deque,
-                     const StringPool* string_pool);
-  ~StringPoolAccessor() override;
-
-  uint32_t Size() const override {
-    return static_cast<uint32_t>(deque_->size());
-  }
-
-  NullTermStringView Get(uint32_t idx) const override {
-    return string_pool_->Get((*deque_)[idx]);
-  }
-
- private:
-  const std::deque<StringPool::Id>* deque_;
-  const StringPool* string_pool_;
-};
-
-// An accessor implementation for string which uses a deque to store indices
-// into a vector of strings.
-template <typename Id>
-class StringVectorAccessor : public Accessor<NullTermStringView> {
- public:
-  StringVectorAccessor(const std::deque<Id>* deque,
-                       const std::vector<NullTermStringView>* string_map)
-      : deque_(deque), string_map_(string_map) {}
-  ~StringVectorAccessor() override = default;
-
-  uint32_t Size() const override {
-    return static_cast<uint32_t>(deque_->size());
-  }
-
-  NullTermStringView Get(uint32_t idx) const override {
-    return (*string_map_)[static_cast<size_t>((*deque_)[idx])];
-  }
-
- private:
-  const std::deque<Id>* deque_;
-  const std::vector<NullTermStringView>* string_map_;
-};
-
-// An accessor implementation for numeric columns which uses a deque as the
-// backing storage with an opitonal index for quick equality filtering.
-template <typename NumericType>
-class NumericDequeAccessor : public Accessor<NumericType> {
- public:
-  NumericDequeAccessor(const std::deque<NumericType>* deque,
-                       const std::deque<std::vector<uint32_t>>* index,
-                       bool has_ordering)
-      : deque_(deque), index_(index), has_ordering_(has_ordering) {}
-  ~NumericDequeAccessor() override = default;
-
-  uint32_t Size() const override {
-    return static_cast<uint32_t>(deque_->size());
-  }
-
-  NumericType Get(uint32_t idx) const override { return (*deque_)[idx]; }
-
-  bool HasOrdering() const override { return has_ordering_; }
-
-  uint32_t LowerBoundIndex(NumericType value) const override {
-    PERFETTO_DCHECK(HasOrdering());
-    auto it = std::lower_bound(deque_->begin(), deque_->end(), value);
-    return static_cast<uint32_t>(std::distance(deque_->begin(), it));
-  }
-
-  uint32_t UpperBoundIndex(NumericType value) const override {
-    PERFETTO_DCHECK(HasOrdering());
-    auto it = std::upper_bound(deque_->begin(), deque_->end(), value);
-    return static_cast<uint32_t>(std::distance(deque_->begin(), it));
-  }
-
-  bool CanFindEqualIndices() const override {
-    return std::is_integral<NumericType>::value && index_ != nullptr;
-  }
-
-  std::vector<uint32_t> EqualIndices(NumericType value) const override {
-    PERFETTO_DCHECK(CanFindEqualIndices());
-    if (value < 0 || static_cast<size_t>(value) >= index_->size())
-      return {};
-    return (*index_)[static_cast<size_t>(value)];
-  }
-
- private:
-  const std::deque<NumericType>* deque_ = nullptr;
-  const std::deque<std::vector<uint32_t>>* index_ = nullptr;
-  bool has_ordering_ = false;
-};
-
-class TsEndAccessor : public Accessor<int64_t> {
- public:
-  TsEndAccessor(const std::deque<int64_t>* ts, const std::deque<int64_t>* dur);
-  ~TsEndAccessor() override;
-
-  uint32_t Size() const override { return static_cast<uint32_t>(ts_->size()); }
-
-  int64_t Get(uint32_t idx) const override {
-    return (*ts_)[idx] + (*dur_)[idx];
-  }
-
- private:
-  const std::deque<int64_t>* ts_ = nullptr;
-  const std::deque<int64_t>* dur_ = nullptr;
-};
-
-class RowAccessor : public Accessor<uint32_t> {
- public:
-  RowAccessor();
-  ~RowAccessor() override;
-
-  uint32_t Size() const override {
-    return std::numeric_limits<uint32_t>::max();
-  }
-
-  uint32_t Get(uint32_t idx) const override { return idx; }
-
-  bool HasOrdering() const override { return true; }
-
-  uint32_t LowerBoundIndex(uint32_t idx) const override { return idx; }
-
-  uint32_t UpperBoundIndex(uint32_t idx) const override { return idx + 1; }
-};
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_STORAGE_COLUMNS_H_
diff --git a/src/trace_processor/storage_schema.cc b/src/trace_processor/storage_schema.cc
deleted file mode 100644
index 7c43ea7..0000000
--- a/src/trace_processor/storage_schema.cc
+++ /dev/null
@@ -1,50 +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/storage_schema.h"
-
-#include "src/trace_processor/row_iterators.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-StorageSchema::StorageSchema() = default;
-StorageSchema::StorageSchema(Columns columns,
-                             std::vector<std::string> primary_keys)
-    : columns_(std::move(columns)), primary_keys_(std::move(primary_keys)) {}
-
-SqliteTable::Schema StorageSchema::ToTableSchema() {
-  std::vector<SqliteTable::Column> columns;
-  size_t i = 0;
-  for (const auto& col : columns_)
-    columns.emplace_back(i++, col->name(), col->GetType(), col->hidden());
-
-  std::vector<size_t> primary_keys;
-  for (const auto& p_key : primary_keys_)
-    primary_keys.emplace_back(ColumnIndexFromName(p_key));
-  return SqliteTable::Schema(std::move(columns), std::move(primary_keys));
-}
-
-size_t StorageSchema::ColumnIndexFromName(const std::string& name) const {
-  auto p = [name](const std::unique_ptr<StorageColumn>& col) {
-    return name == col->name();
-  };
-  auto it = std::find_if(columns_.begin(), columns_.end(), p);
-  return static_cast<size_t>(std::distance(columns_.begin(), it));
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/storage_schema.h b/src/trace_processor/storage_schema.h
deleted file mode 100644
index db032bc..0000000
--- a/src/trace_processor/storage_schema.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * 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_STORAGE_SCHEMA_H_
-#define SRC_TRACE_PROCESSOR_STORAGE_SCHEMA_H_
-
-#include <algorithm>
-#include <deque>
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "src/trace_processor/filtered_row_index.h"
-#include "src/trace_processor/sqlite/sqlite_table.h"
-#include "src/trace_processor/sqlite/sqlite_utils.h"
-#include "src/trace_processor/storage_columns.h"
-#include "src/trace_processor/trace_storage.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-// Defines the schema for a table which is backed by concrete storage (i.e. does
-// not generate data on the fly).
-// Used by all tables which are backed by data in TraceStorage.
-class StorageSchema {
- public:
-  using Columns = std::vector<std::unique_ptr<StorageColumn>>;
-
-  // Builder class for StorageSchema.
-  class Builder {
-   public:
-    template <class T, class... Args>
-    Builder& AddColumn(std::string column_name, Args&&... args) {
-      columns_.emplace_back(new T(column_name, std::forward<Args>(args)...));
-      return *this;
-    }
-
-    template <class NumericType>
-    Builder& AddNumericColumn(
-        std::string column_name,
-        const std::deque<NumericType>* vals,
-        const std::deque<std::vector<uint32_t>>* index = nullptr) {
-      NumericDequeAccessor<NumericType> accessor(vals, index,
-                                                 false /* has_ordering */);
-      return AddGenericNumericColumn(column_name, accessor);
-    }
-
-    template <class NumericType>
-    Builder& AddOrderedNumericColumn(std::string column_name,
-                                     const std::deque<NumericType>* vals) {
-      NumericDequeAccessor<NumericType> accessor(vals, nullptr,
-                                                 true /* has_ordering */);
-      return AddGenericNumericColumn(column_name, accessor);
-    }
-
-    template <class Accessor>
-    Builder& AddGenericNumericColumn(std::string column_name,
-                                     Accessor accessor) {
-      columns_.emplace_back(new NumericStorageColumn<decltype(accessor)>(
-          column_name, false /* hidden */, accessor));
-      return *this;
-    }
-
-    template <class Id>
-    Builder& AddStringColumn(
-        std::string column_name,
-        const std::deque<Id>* ids,
-        const std::vector<NullTermStringView>* string_map) {
-      StringVectorAccessor<Id> accessor(ids, string_map);
-      columns_.emplace_back(
-          new StringColumn<decltype(accessor)>(column_name, accessor));
-      return *this;
-    }
-
-    Builder& AddStringColumn(std::string column_name,
-                             const std::deque<StringPool::Id>* ids,
-                             const StringPool* string_pool) {
-      StringPoolAccessor accessor(ids, string_pool);
-      columns_.emplace_back(
-          new StringColumn<StringPoolAccessor>(column_name, accessor));
-      return *this;
-    }
-
-    StorageSchema Build(std::vector<std::string> primary_keys) {
-      return StorageSchema(std::move(columns_), std::move(primary_keys));
-    }
-
-   private:
-    Columns columns_;
-  };
-
-  StorageSchema();
-  StorageSchema(Columns columns, std::vector<std::string> primary_keys);
-
-  SqliteTable::Schema ToTableSchema();
-
-  size_t ColumnIndexFromName(const std::string& name) const;
-
-  const StorageColumn& GetColumn(size_t idx) const { return *(columns_[idx]); }
-
-  Columns* mutable_columns() { return &columns_; }
-
- private:
-  friend class Builder;
-
-  Columns columns_;
-  std::vector<std::string> primary_keys_;
-};
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_STORAGE_SCHEMA_H_
diff --git a/src/trace_processor/storage_table.cc b/src/trace_processor/storage_table.cc
deleted file mode 100644
index 09300d2..0000000
--- a/src/trace_processor/storage_table.cc
+++ /dev/null
@@ -1,198 +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/storage_table.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-StorageTable::StorageTable() = default;
-StorageTable::~StorageTable() = default;
-
-util::Status StorageTable::Init(int, const char* const*, Schema* schema) {
-  schema_ = CreateStorageSchema();
-  *schema = schema_.ToTableSchema();
-  return util::OkStatus();
-}
-
-std::unique_ptr<SqliteTable::Cursor> StorageTable::CreateCursor() {
-  return std::unique_ptr<Cursor>(new Cursor(this));
-}
-
-std::unique_ptr<RowIterator> StorageTable::CreateBestRowIterator(
-    const QueryConstraints& qc,
-    sqlite3_value** argv) {
-  const auto& cs = qc.constraints();
-  auto obs = RemoveRedundantOrderBy(cs, qc.order_by());
-
-  // Figure out whether the data is already ordered and which order we should
-  // traverse the data.
-  bool is_ordered, is_desc = false;
-  std::tie(is_ordered, is_desc) = IsOrdered(obs);
-
-  // Create the range iterator and if we are sorted, just return it.
-  auto index = CreateRangeIterator(cs, argv);
-  if (!index.error().empty()) {
-    SetErrorMessage(sqlite3_mprintf(index.error().c_str()));
-    return nullptr;
-  }
-
-  if (is_ordered)
-    return index.ToRowIterator(is_desc);
-
-  // Otherwise, create the sorted vector of indices and create the vector
-  // iterator.
-  return std::unique_ptr<VectorRowIterator>(
-      new VectorRowIterator(CreateSortedIndexVector(std::move(index), obs)));
-}
-
-FilteredRowIndex StorageTable::CreateRangeIterator(
-    const std::vector<QueryConstraints::Constraint>& cs,
-    sqlite3_value** argv) {
-  // Try and bound the search space to the smallest possible index region and
-  // store any leftover constraints to filter using bitvector.
-  uint32_t min_idx = 0;
-  uint32_t max_idx = RowCount();
-  std::vector<size_t> bitvector_cs;
-  for (size_t i = 0; i < cs.size(); i++) {
-    const auto& c = cs[i];
-    size_t column = static_cast<size_t>(c.column);
-    auto bounds = schema_.GetColumn(column).BoundFilter(c.op, argv[i]);
-
-    min_idx = std::max(min_idx, bounds.min_idx);
-    max_idx = std::min(max_idx, bounds.max_idx);
-
-    // If the lower bound is higher than the upper bound, return a zero-sized
-    // range iterator.
-    if (min_idx >= max_idx)
-      return FilteredRowIndex(min_idx, min_idx);
-
-    if (!bounds.consumed)
-      bitvector_cs.emplace_back(i);
-  }
-
-  // Create an filter index and allow each of the columns filter on it.
-  FilteredRowIndex index(min_idx, max_idx);
-  for (const auto& c_idx : bitvector_cs) {
-    const auto& c = cs[c_idx];
-    auto* value = argv[c_idx];
-
-    const auto& schema_col = schema_.GetColumn(static_cast<size_t>(c.column));
-    schema_col.Filter(c.op, value, &index);
-
-    if (!index.error().empty())
-      break;
-  }
-  return index;
-}
-
-std::pair<bool, bool> StorageTable::IsOrdered(
-    const std::vector<QueryConstraints::OrderBy>& obs) {
-  if (obs.size() == 0)
-    return std::make_pair(true, false);
-
-  if (obs.size() != 1)
-    return std::make_pair(false, false);
-
-  const auto& ob = obs[0];
-  auto col = static_cast<size_t>(ob.iColumn);
-  return std::make_pair(schema_.GetColumn(col).HasOrdering(), ob.desc);
-}
-
-std::vector<QueryConstraints::OrderBy> StorageTable::RemoveRedundantOrderBy(
-    const std::vector<QueryConstraints::Constraint>& cs,
-    const std::vector<QueryConstraints::OrderBy>& obs) {
-  std::vector<QueryConstraints::OrderBy> filtered;
-  std::set<int> equality_cols;
-  for (const auto& c : cs) {
-    if (sqlite_utils::IsOpEq(c.op))
-      equality_cols.emplace(c.column);
-  }
-  for (const auto& o : obs) {
-    if (equality_cols.count(o.iColumn) > 0)
-      continue;
-    filtered.emplace_back(o);
-  }
-  return filtered;
-}
-
-std::vector<uint32_t> StorageTable::CreateSortedIndexVector(
-    FilteredRowIndex index,
-    const std::vector<QueryConstraints::OrderBy>& obs) {
-  PERFETTO_DCHECK(obs.size() > 0);
-
-  // Retrieve the index created above from the index.
-  std::vector<uint32_t> sorted_rows = index.ToRowVector();
-
-  std::vector<StorageColumn::Comparator> comparators;
-  for (const auto& ob : obs) {
-    auto col = static_cast<size_t>(ob.iColumn);
-    comparators.emplace_back(schema_.GetColumn(col).Sort(ob));
-  }
-
-  auto comparator = [&comparators](uint32_t f, uint32_t s) {
-    for (const auto& comp : comparators) {
-      int c = comp(f, s);
-      if (c != 0)
-        return c < 0;
-    }
-    return false;
-  };
-  std::sort(sorted_rows.begin(), sorted_rows.end(), comparator);
-
-  return sorted_rows;
-}
-
-bool StorageTable::HasEqConstraint(const QueryConstraints& qc,
-                                   const std::string& col_name) {
-  size_t c_idx = schema().ColumnIndexFromName(col_name);
-  auto fn = [c_idx](const QueryConstraints::Constraint& c) {
-    return c.column == static_cast<int>(c_idx) && sqlite_utils::IsOpEq(c.op);
-  };
-  const auto& cs = qc.constraints();
-  return std::find_if(cs.begin(), cs.end(), fn) != cs.end();
-}
-
-StorageTable::Cursor::Cursor(StorageTable* table)
-    : SqliteTable::Cursor(table), table_(table) {}
-
-int StorageTable::Cursor::Filter(const QueryConstraints& qc,
-                                 sqlite3_value** argv,
-                                 FilterHistory) {
-  iterator_ = table_->CreateBestRowIterator(qc, argv);
-  if (!iterator_)
-    return SQLITE_ERROR;
-  columns_ = table_->schema_.mutable_columns();
-  return SQLITE_OK;
-}
-
-int StorageTable::Cursor::Next() {
-  iterator_->NextRow();
-  return SQLITE_OK;
-}
-
-int StorageTable::Cursor::Eof() {
-  return iterator_->IsEnd();
-}
-
-int StorageTable::Cursor::Column(sqlite3_context* context, int raw_col) {
-  size_t column = static_cast<size_t>(raw_col);
-  (*columns_)[column]->ReportResult(context, iterator_->Row());
-  return SQLITE_OK;
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/storage_table.h b/src/trace_processor/storage_table.h
deleted file mode 100644
index 882ee32..0000000
--- a/src/trace_processor/storage_table.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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_STORAGE_TABLE_H_
-#define SRC_TRACE_PROCESSOR_STORAGE_TABLE_H_
-
-#include <set>
-
-#include "src/trace_processor/row_iterators.h"
-#include "src/trace_processor/sqlite/sqlite_table.h"
-#include "src/trace_processor/storage_columns.h"
-#include "src/trace_processor/storage_schema.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-// Base class for all table implementations which are backed by some data
-// storage.
-class StorageTable : public SqliteTable {
- public:
-  // A cursor which abstracts common patterns found in storage backed tables. It
-  // takes a strategy to iterate through rows and a column reporter for each
-  // column to implement the Cursor interface.
-  class Cursor final : public SqliteTable::Cursor {
-   public:
-    Cursor(StorageTable* table);
-
-    // Implementation of SqliteTable::Cursor.
-    int Filter(const QueryConstraints& qc,
-               sqlite3_value** argv,
-               FilterHistory) override;
-    int Next() override;
-    int Eof() override;
-    int Column(sqlite3_context*, int N) override;
-
-   private:
-    std::unique_ptr<RowIterator> iterator_;
-    std::vector<std::unique_ptr<StorageColumn>>* columns_ = nullptr;
-    StorageTable* table_ = nullptr;
-  };
-
-  StorageTable();
-  virtual ~StorageTable() override;
-
-  // Table implementation.
-  util::Status Init(int,
-                    const char* const*,
-                    SqliteTable::Schema*) override final;
-  std::unique_ptr<SqliteTable::Cursor> CreateCursor() override;
-
-  // Required methods for subclasses to implement.
-  virtual StorageSchema CreateStorageSchema() = 0;
-  virtual uint32_t RowCount() = 0;
-
- protected:
-  const StorageSchema& schema() const { return schema_; }
-
-  bool HasEqConstraint(const QueryConstraints&, const std::string& col_name);
-
- private:
-  // Creates a row iterator which is optimized for a generic storage schema
-  // (i.e. it does not make assumptions about values of columns).
-  std::unique_ptr<RowIterator> CreateBestRowIterator(const QueryConstraints& qc,
-                                                     sqlite3_value** argv);
-
-  FilteredRowIndex CreateRangeIterator(
-      const std::vector<QueryConstraints::Constraint>& cs,
-      sqlite3_value** argv);
-
-  std::pair<bool, bool> IsOrdered(
-      const std::vector<QueryConstraints::OrderBy>& obs);
-
-  std::vector<QueryConstraints::OrderBy> RemoveRedundantOrderBy(
-      const std::vector<QueryConstraints::Constraint>& cs,
-      const std::vector<QueryConstraints::OrderBy>& obs);
-
-  std::vector<uint32_t> CreateSortedIndexVector(
-      FilteredRowIndex index,
-      const std::vector<QueryConstraints::OrderBy>& obs);
-
-  StorageSchema schema_;
-};
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_STORAGE_TABLE_H_
diff --git a/src/trace_processor/tables/BUILD.gn b/src/trace_processor/tables/BUILD.gn
index 268e419..7f6ac2b 100644
--- a/src/trace_processor/tables/BUILD.gn
+++ b/src/trace_processor/tables/BUILD.gn
@@ -23,6 +23,7 @@
     "metadata_tables.h",
     "profiler_tables.h",
     "slice_tables.h",
+    "table_destructors.cc",
     "track_tables.h",
   ]
   deps = [
@@ -33,9 +34,7 @@
 
 source_set("unittests") {
   testonly = true
-  sources = [
-    "macros_unittest.cc",
-  ]
+  sources = [ "macros_unittest.cc" ]
   deps = [
     ":tables",
     "../../../gn:default_deps",
@@ -51,8 +50,6 @@
       "../../../gn:benchmark",
       "../../../gn:default_deps",
     ]
-    sources = [
-      "macros_benchmark.cc",
-    ]
+    sources = [ "macros_benchmark.cc" ]
   }
 }
diff --git a/src/trace_processor/tables/counter_tables.h b/src/trace_processor/tables/counter_tables.h
index 12900f5..a3cd5ab 100644
--- a/src/trace_processor/tables/counter_tables.h
+++ b/src/trace_processor/tables/counter_tables.h
@@ -18,6 +18,7 @@
 #define SRC_TRACE_PROCESSOR_TABLES_COUNTER_TABLES_H_
 
 #include "src/trace_processor/tables/macros.h"
+#include "src/trace_processor/tables/track_tables.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -27,7 +28,7 @@
   NAME(CounterTable, "counter")                        \
   PERFETTO_TP_ROOT_TABLE(PARENT, C)                    \
   C(int64_t, ts, Column::Flag::kSorted)                \
-  C(uint32_t, track_id)                                \
+  C(CounterTrackTable::Id, track_id)                   \
   C(double, value)                                     \
   C(base::Optional<uint32_t>, arg_set_id)
 
diff --git a/src/trace_processor/tables/macros_benchmark.cc b/src/trace_processor/tables/macros_benchmark.cc
index 2e7bfbb..01bc9f0 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);
@@ -40,6 +41,9 @@
 
 PERFETTO_TP_TABLE(PERFETTO_TP_CHILD_TABLE);
 
+RootTestTable::~RootTestTable() = default;
+ChildTestTable::~ChildTestTable() = default;
+
 }  // namespace
 }  // namespace trace_processor
 }  // namespace perfetto
@@ -72,8 +76,10 @@
 
 using perfetto::trace_processor::ChildTestTable;
 using perfetto::trace_processor::RootTestTable;
+using perfetto::trace_processor::RowMap;
 using perfetto::trace_processor::SqlValue;
 using perfetto::trace_processor::StringPool;
+using perfetto::trace_processor::Table;
 
 static void BM_TableInsert(benchmark::State& state) {
   StringPool pool;
@@ -108,6 +114,30 @@
 }
 BENCHMARK(BM_TableIteratorChild)->Apply(TableFilterArgs);
 
+static void BM_TableFilterAndSortRoot(benchmark::State& state) {
+  StringPool pool;
+  RootTestTable root(&pool, nullptr);
+
+  uint32_t size = static_cast<uint32_t>(state.range(0));
+  uint32_t partitions = 8;
+
+  std::minstd_rand0 rnd_engine(45);
+  for (uint32_t i = 0; i < size; ++i) {
+    RootTestTable::Row row;
+    row.root_non_null = rnd_engine() % partitions;
+    row.root_non_null_2 = static_cast<uint32_t>(rnd_engine());
+    root.Insert(row);
+  }
+
+  for (auto _ : state) {
+    Table filtered = root.Filter({root.root_non_null().eq(5)},
+                                 RowMap::OptimizeFor::kLookupSpeed);
+    benchmark::DoNotOptimize(
+        filtered.Sort({root.root_non_null_2().ascending()}));
+  }
+}
+BENCHMARK(BM_TableFilterAndSortRoot)->Apply(TableFilterArgs);
+
 static void BM_TableFilterRootId(benchmark::State& state) {
   StringPool pool;
   RootTestTable root(&pool, nullptr);
@@ -200,6 +230,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 +388,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 a4fbab7..35bbec8 100644
--- a/src/trace_processor/tables/macros_internal.h
+++ b/src/trace_processor/tables/macros_internal.h
@@ -40,7 +40,15 @@
    protected:
     const char* type_ = nullptr;
   };
-  uint32_t Insert(const Row&) { PERFETTO_FATAL("Should not be called"); }
+  // This class only exists to allow typechecking to work correctly in Insert
+  // below. If we had C++17 and if constexpr, we could statically verify that
+  // this was never created but for now, we still need to define it to satisfy
+  // the typechecker.
+  struct IdAndRow {
+    uint32_t id;
+    uint32_t row;
+  };
+  IdAndRow Insert(const Row&) { PERFETTO_FATAL("Should not be called"); }
 };
 
 // IdHelper is used to figure out the Id type for a table.
@@ -85,6 +93,7 @@
                  static_cast<uint32_t>(row_maps_.size()) - 1));
     }
   }
+  ~MacroTable() override;
 
   const char* table_name() const { return name_; }
 
@@ -194,37 +203,42 @@
 
 // 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)          \
-  columns_.emplace_back(                                                       \
-      #name, &name##_,                                                         \
-      static_cast<uint32_t>(flags) | TypedColumn<type>::default_flags(), this, \
-      columns_.size(), row_maps_.size() - 1);
+#define PERFETTO_TP_COLUMN_FLAG_HAS_FLAG_COL(type, name, flags) \
+  case ColumnIndex::name:                                       \
+    return static_cast<uint32_t>(flags) | TypedColumn<type>::default_flags();
 
-// Constructs the column in the Table constructor when no flags are specified.
-#define PERFETTO_TP_TABLE_CONSTRUCTOR_COLUMN_NO_FLAGS(type, name)            \
-  columns_.emplace_back(#name, &name##_, TypedColumn<type>::default_flags(), \
-                        this, columns_.size(), row_maps_.size() - 1);
+#define PERFETTO_TP_COLUMN_FLAG_NO_FLAG_COL(type, name) \
+  case ColumnIndex::name:                               \
+    return TypedColumn<type>::default_flags();
 
-// Chooses between the flag and no-flag variant based on the whether there
-// are two or three arguments.
-#define PERFETTO_TP_TABLE_CONSTRUCTOR_COLUMN_CHOOSER(type, name, maybe_flags, \
-                                                     fn, ...)                 \
-  fn
+#define PERFETTO_TP_COLUMN_FLAG_CHOOSER(type, name, maybe_flags, fn, ...) fn
 
-// Invokes the chosen column constructor by passing the given args.
-#define PERFETTO_TP_TABLE_CONSTRUCTOR_COLUMN(...)              \
-  PERFETTO_TP_TABLE_CONSTRUCTOR_COLUMN_CHOOSER(                \
-      __VA_ARGS__, PERFETTO_TP_TABLE_CONSTRUCTOR_COLUMN_FLAGS, \
-      PERFETTO_TP_TABLE_CONSTRUCTOR_COLUMN_NO_FLAGS)           \
+#define PERFETTO_TP_COLUMN_FLAG(...)                                    \
+  PERFETTO_TP_COLUMN_FLAG_CHOOSER(__VA_ARGS__,                          \
+                                  PERFETTO_TP_COLUMN_FLAG_HAS_FLAG_COL, \
+                                  PERFETTO_TP_COLUMN_FLAG_NO_FLAG_COL)  \
   (__VA_ARGS__)
 
-// Inserts the value into the corresponding column
+// Invokes the chosen column constructor by passing the given args.
+#define PERFETTO_TP_TABLE_CONSTRUCTOR_COLUMN(type, name, ...)               \
+  columns_.emplace_back(#name, &name##_, FlagsForColumn(ColumnIndex::name), \
+                        this, columns_.size(), row_maps_.size() - 1);
+
+// Inserts the value into the corresponding column.
 #define PERFETTO_TP_COLUMN_APPEND(type, name, ...) \
   mutable_##name()->Append(std::move(row.name));
 
+// Creates a schema entry for the corresponding column.
+#define PERFETTO_TP_COLUMN_SCHEMA(type, name, ...)          \
+  schema.columns.emplace_back(Table::Schema::Column{        \
+      #name, TypedColumn<type>::SqlValueType(), false,      \
+      static_cast<bool>(FlagsForColumn(ColumnIndex::name) & \
+                        Column::Flag::kSorted),             \
+      static_cast<bool>(FlagsForColumn(ColumnIndex::name) & \
+                        Column::Flag::kHidden)});
+
 // Defines the accessors for a column.
 #define PERFETTO_TP_TABLE_COL_ACCESSOR(type, name, ...)       \
   const TypedColumn<type>& name() const {                     \
@@ -259,20 +273,15 @@
      * using declaration of Id below.                                         \
      * Note: This type will only used if this table is a root table.          \
      */                                                                       \
-    struct DefinedId {                                                        \
+    struct DefinedId : public BaseId {                                        \
       DefinedId() = default;                                                  \
-      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;                                                         \
+      explicit constexpr DefinedId(uint32_t v) : BaseId(v) {}                 \
     };                                                                        \
                                                                               \
    public:                                                                    \
     /*                                                                        \
-     * This defines the type of the id to be the type of the root table of    \
-     * the hierarchy - see IdHelper for more details.                         \
+     * This defines the type of the id to be the type of the root             \
+     * table of the hierarchy - see IdHelper for more details.                \
      */                                                                       \
     using Id = macros_internal::IdHelper<parent_class_name, class_name>::Id;  \
     struct Row : parent_class_name::Row {                                     \
@@ -315,6 +324,12 @@
       PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_NAME_COMMA) kNumCols           \
     };                                                                        \
                                                                               \
+    /* Return value of Insert giving access to id and row number */           \
+    struct IdAndRow {                                                         \
+      Id id;                                                                  \
+      uint32_t row;                                                           \
+    };                                                                        \
+                                                                              \
     class_name(StringPool* pool, parent_class_name* parent)                   \
         : macros_internal::MacroTable(table_name, pool, parent),              \
           parent_(parent) {                                                   \
@@ -328,14 +343,16 @@
        */                                                                     \
       PERFETTO_TP_TABLE_COLUMNS(DEF, PERFETTO_TP_TABLE_CONSTRUCTOR_COLUMN);   \
     }                                                                         \
+    ~class_name() override;                                                   \
                                                                               \
-    Id Insert(const Row& row) {                                               \
+    IdAndRow Insert(const Row& row) {                                         \
       Id id;                                                                  \
+      uint32_t row_number = row_count();                                      \
       if (parent_ == nullptr) {                                               \
-        id = Id{row_count()};                                                 \
+        id = Id{row_number};                                                  \
         type_.Append(string_pool_->InternString(row.type()));                 \
       } else {                                                                \
-        id = Id{parent_->Insert(row)};                                        \
+        id = Id{parent_->Insert(row).id};                                     \
       }                                                                       \
       UpdateRowMapsAfterParentInsert();                                       \
                                                                               \
@@ -346,7 +363,7 @@
        * ...                                                                  \
        */                                                                     \
       PERFETTO_TP_TABLE_COLUMNS(DEF, PERFETTO_TP_COLUMN_APPEND);              \
-      return id;                                                              \
+      return {id, row_number};                                                \
     }                                                                         \
                                                                               \
     const IdColumn<Id>& id() const {                                          \
@@ -359,6 +376,16 @@
           columns_[static_cast<uint32_t>(ColumnIndex::type)]);                \
     }                                                                         \
                                                                               \
+    static Table::Schema Schema() {                                           \
+      Table::Schema schema;                                                   \
+      schema.columns.emplace_back(Table::Schema::Column{                      \
+          "id", SqlValue::Type::kLong, true, true, false});                   \
+      schema.columns.emplace_back(Table::Schema::Column{                      \
+          "type", SqlValue::Type::kString, false, false, false});             \
+      PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_COLUMN_SCHEMA);                \
+      return schema;                                                          \
+    }                                                                         \
+                                                                              \
     /*                                                                        \
      * Expands to                                                             \
      * const TypedColumn<col1_type>& col1() { return col1_; }                 \
@@ -370,6 +397,25 @@
     PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_TABLE_COL_ACCESSOR)              \
                                                                               \
    private:                                                                   \
+    static uint32_t FlagsForColumn(const ColumnIndex index) {                 \
+      switch (index) {                                                        \
+        case ColumnIndex::kNumCols:                                           \
+          PERFETTO_FATAL("Invalid index");                                    \
+        case ColumnIndex::id:                                                 \
+          return Column::kIdFlags;                                            \
+        case ColumnIndex::type:                                               \
+          return Column::kNoFlag;                                             \
+          /*                                                                  \
+           * Expands to:                                                      \
+           *  case ColumnIndex::col1:                                         \
+           *    return TypedColumn<col_type1>::default_flags();               \
+           *  ...                                                             \
+           */                                                                 \
+          PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_COLUMN_FLAG)               \
+      }                                                                       \
+      PERFETTO_FATAL("For GCC");                                              \
+    }                                                                         \
+                                                                              \
     parent_class_name* parent_;                                               \
                                                                               \
     /*                                                                        \
diff --git a/src/trace_processor/tables/macros_unittest.cc b/src/trace_processor/tables/macros_unittest.cc
index 9bf0b75..c53096c 100644
--- a/src/trace_processor/tables/macros_unittest.cc
+++ b/src/trace_processor/tables/macros_unittest.cc
@@ -50,6 +50,11 @@
   C(StringPool::Id, end_state)
 PERFETTO_TP_TABLE(PERFETTO_TP_TEST_CPU_SLICE_TABLE_DEF);
 
+TestEventTable::~TestEventTable() = default;
+TestCounterTable::~TestCounterTable() = default;
+TestSliceTable::~TestSliceTable() = default;
+TestCpuSliceTable::~TestCpuSliceTable() = default;
+
 class TableMacrosUnittest : public ::testing::Test {
  protected:
   StringPool pool_;
@@ -67,13 +72,13 @@
 }
 
 TEST_F(TableMacrosUnittest, InsertParent) {
-  auto id = event_.Insert(TestEventTable::Row(100, 0));
+  auto id = event_.Insert(TestEventTable::Row(100, 0)).id;
   ASSERT_EQ(id.value, 0u);
   ASSERT_EQ(event_.type().GetString(0), "event");
   ASSERT_EQ(event_.ts()[0], 100);
   ASSERT_EQ(event_.arg_set_id()[0], 0);
 
-  id = slice_.Insert(TestSliceTable::Row(200, 123, 10, 0));
+  id = slice_.Insert(TestSliceTable::Row(200, 123, 10, 0)).id;
   ASSERT_EQ(id.value, 1u);
 
   ASSERT_EQ(event_.type().GetString(1), "slice");
@@ -85,7 +90,7 @@
   ASSERT_EQ(slice_.dur()[0], 10);
   ASSERT_EQ(slice_.depth()[0], 0);
 
-  id = slice_.Insert(TestSliceTable::Row(210, 456, base::nullopt, 0));
+  id = slice_.Insert(TestSliceTable::Row(210, 456, base::nullopt, 0)).id;
   ASSERT_EQ(id.value, 2u);
 
   ASSERT_EQ(event_.type().GetString(2), "slice");
@@ -103,8 +108,9 @@
   slice_.Insert(TestSliceTable::Row(200, 123, 10, 0));
 
   auto reason = pool_.InternString("R");
-  auto id = cpu_slice_.Insert(
-      TestCpuSliceTable::Row(205, 456, 5, 1, 4, 1024, reason));
+  auto id =
+      cpu_slice_.Insert(TestCpuSliceTable::Row(205, 456, 5, 1, 4, 1024, reason))
+          .id;
   ASSERT_EQ(id.value, 2u);
   ASSERT_EQ(event_.type().GetString(2), "cpu_slice");
   ASSERT_EQ(event_.ts()[2], 205);
diff --git a/src/trace_processor/tables/metadata_tables.h b/src/trace_processor/tables/metadata_tables.h
index dfcb98b..d1a9283 100644
--- a/src/trace_processor/tables/metadata_tables.h
+++ b/src/trace_processor/tables/metadata_tables.h
@@ -76,7 +76,8 @@
   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)
+  C(base::Optional<uint32_t>, uid)                     \
+  C(base::Optional<uint32_t>, android_appid)
 
 PERFETTO_TP_TABLE(PERFETTO_TP_PROCESS_TABLE_DEF);
 
diff --git a/src/trace_processor/tables/profiler_tables.h b/src/trace_processor/tables/profiler_tables.h
index 04dae8d..9c5b840 100644
--- a/src/trace_processor/tables/profiler_tables.h
+++ b/src/trace_processor/tables/profiler_tables.h
@@ -23,33 +23,28 @@
 namespace trace_processor {
 namespace tables {
 
-#define PERFETTO_TP_STACK_PROFILE_CALLSITE_DEF(NAME, PARENT, C) \
-  NAME(StackProfileCallsiteTable, "stack_profile_callsite")     \
-  PERFETTO_TP_ROOT_TABLE(PARENT, C)                             \
-  C(int64_t, depth)                                             \
-  C(int64_t, parent_id)                                         \
-  C(int64_t, frame_id)
+#define PERFETTO_TP_PROFILER_SMAPS_DEF(NAME, PARENT, C) \
+  NAME(ProfilerSmapsTable, "profiler_smaps")            \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                     \
+  C(uint32_t, upid)                                     \
+  C(int64_t, ts)                                        \
+  C(StringPool::Id, path)                               \
+  C(int64_t, size_kb)                                   \
+  C(int64_t, private_dirty_kb)                          \
+  C(int64_t, swap_kb)
 
-PERFETTO_TP_TABLE(PERFETTO_TP_STACK_PROFILE_CALLSITE_DEF);
+PERFETTO_TP_TABLE(PERFETTO_TP_PROFILER_SMAPS_DEF);
 
-#define PERFETTO_TP_CPU_PROFILE_STACK_SAMPLE_DEF(NAME, PARENT, C) \
-  NAME(CpuProfileStackSampleTable, "cpu_profile_stack_sample")    \
-  PERFETTO_TP_ROOT_TABLE(PARENT, C)                               \
-  C(int64_t, ts, Column::Flag::kSorted)                           \
-  C(int64_t, callsite_id)                                         \
-  C(uint32_t, utid)
+#define PERFETTO_TP_PACKAGES_LIST_DEF(NAME, PARENT, C) \
+  NAME(PackageListTable, "package_list")               \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                    \
+  C(StringPool::Id, package_name)                      \
+  C(int64_t, uid)                                      \
+  C(int32_t, debuggable)                               \
+  C(int32_t, profileable_from_shell)                   \
+  C(int64_t, version_code)
 
-PERFETTO_TP_TABLE(PERFETTO_TP_CPU_PROFILE_STACK_SAMPLE_DEF);
-
-#define PERFETTO_TP_SYMBOL_DEF(NAME, PARENT, C) \
-  NAME(SymbolTable, "stack_profile_symbol")     \
-  PERFETTO_TP_ROOT_TABLE(PARENT, C)             \
-  C(uint32_t, symbol_set_id)                    \
-  C(StringPool::Id, name)                       \
-  C(StringPool::Id, source_file)                \
-  C(uint32_t, line_number)
-
-PERFETTO_TP_TABLE(PERFETTO_TP_SYMBOL_DEF);
+PERFETTO_TP_TABLE(PERFETTO_TP_PACKAGES_LIST_DEF);
 
 #define PERFETTO_TP_STACK_PROFILE_MAPPING_DEF(NAME, PARENT, C) \
   NAME(StackProfileMappingTable, "stack_profile_mapping")      \
@@ -68,31 +63,59 @@
   NAME(StackProfileFrameTable, "stack_profile_frame")        \
   PERFETTO_TP_ROOT_TABLE(PARENT, C)                          \
   C(StringPool::Id, name)                                    \
-  C(int64_t, mapping)                                        \
+  C(StackProfileMappingTable::Id, mapping)                   \
   C(int64_t, rel_pc)                                         \
   C(base::Optional<uint32_t>, symbol_set_id)
 
 PERFETTO_TP_TABLE(PERFETTO_TP_STACK_PROFILE_FRAME_DEF);
 
+#define PERFETTO_TP_STACK_PROFILE_CALLSITE_DEF(NAME, PARENT, C) \
+  NAME(StackProfileCallsiteTable, "stack_profile_callsite")     \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                             \
+  C(uint32_t, depth)                                            \
+  C(base::Optional<StackProfileCallsiteTable::Id>, parent_id)   \
+  C(StackProfileFrameTable::Id, frame_id)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_STACK_PROFILE_CALLSITE_DEF);
+
+#define PERFETTO_TP_CPU_PROFILE_STACK_SAMPLE_DEF(NAME, PARENT, C) \
+  NAME(CpuProfileStackSampleTable, "cpu_profile_stack_sample")    \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                               \
+  C(int64_t, ts, Column::Flag::kSorted)                           \
+  C(StackProfileCallsiteTable::Id, callsite_id)                   \
+  C(uint32_t, utid)                                               \
+  C(int32_t, process_priority)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_CPU_PROFILE_STACK_SAMPLE_DEF);
+
+#define PERFETTO_TP_SYMBOL_DEF(NAME, PARENT, C) \
+  NAME(SymbolTable, "stack_profile_symbol")     \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)             \
+  C(uint32_t, symbol_set_id)                    \
+  C(StringPool::Id, name)                       \
+  C(StringPool::Id, source_file)                \
+  C(uint32_t, line_number)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_SYMBOL_DEF);
+
 #define PERFETTO_TP_HEAP_PROFILE_ALLOCATION_DEF(NAME, PARENT, C) \
   NAME(HeapProfileAllocationTable, "heap_profile_allocation")    \
   PERFETTO_TP_ROOT_TABLE(PARENT, C)                              \
   C(int64_t, ts, Column::Flag::kSorted)                          \
   C(uint32_t, upid)                                              \
-  C(int64_t, callsite_id)                                        \
+  C(StackProfileCallsiteTable::Id, callsite_id)                  \
   C(int64_t, count)                                              \
   C(int64_t, size)
 
 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(int64_t, ts, Column::Flag::kSorted | Column::Flag::kHidden)           \
+  C(uint32_t, upid, Column::Flag::kHidden)                                \
+  C(StringPool::Id, profile_type, Column::Flag::kHidden)                  \
+  C(StringPool::Id, focus_str, Column::Flag::kHidden)                     \
   C(uint32_t, depth)                                                      \
   C(StringPool::Id, name)                                                 \
   C(StringPool::Id, map_name)                                             \
@@ -104,23 +127,31 @@
   C(int64_t, cumulative_alloc_count)                                      \
   C(int64_t, alloc_size)                                                  \
   C(int64_t, cumulative_alloc_size)                                       \
-  C(base::Optional<uint32_t>, parent_id)
+  C(base::Optional<ExperimentalFlamegraphNodesTable::Id>, 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)                         \
-  C(int64_t, upid)                                          \
-  C(int64_t, graph_sample_ts)                               \
-  C(int64_t, object_id)                                     \
-  C(int64_t, self_size)                                     \
-  C(int64_t, retained_size)                                 \
-  C(int64_t, unique_retained_size)                          \
-  C(int64_t, reference_set_id)                              \
-  C(int32_t, reachable)                                     \
-  C(StringPool::Id, type_name)                              \
-  C(base::Optional<StringPool::Id>, deobfuscated_type_name) \
+#define PERFETTO_TP_HEAP_GRAPH_CLASS_DEF(NAME, PARENT, C) \
+  NAME(HeapGraphClassTable, "heap_graph_class")           \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                       \
+  C(StringPool::Id, name)                                 \
+  C(base::Optional<StringPool::Id>, deobfuscated_name)    \
+  C(base::Optional<StringPool::Id>, location)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_HEAP_GRAPH_CLASS_DEF);
+
+#define PERFETTO_TP_HEAP_GRAPH_OBJECT_DEF(NAME, PARENT, C) \
+  NAME(HeapGraphObjectTable, "heap_graph_object")          \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                        \
+  C(uint32_t, upid)                                        \
+  C(int64_t, graph_sample_ts)                              \
+  C(int64_t, object_id)                                    \
+  C(int64_t, self_size)                                    \
+  C(int64_t, retained_size)                                \
+  C(int64_t, unique_retained_size)                         \
+  C(base::Optional<uint32_t>, reference_set_id)            \
+  C(int32_t, reachable)                                    \
+  C(HeapGraphClassTable::Id, type_id)                      \
   C(base::Optional<StringPool::Id>, root_type)
 
 PERFETTO_TP_TABLE(PERFETTO_TP_HEAP_GRAPH_OBJECT_DEF);
@@ -128,10 +159,11 @@
 #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, Column::Flag::kSorted)         \
+  C(uint32_t, reference_set_id, Column::Flag::kSorted)        \
   C(int64_t, owner_id)                                        \
   C(int64_t, owned_id)                                        \
   C(StringPool::Id, field_name)                               \
+  C(StringPool::Id, field_type_name)                          \
   C(base::Optional<StringPool::Id>, deobfuscated_field_name)
 
 PERFETTO_TP_TABLE(PERFETTO_TP_HEAP_GRAPH_REFERENCE_DEF);
diff --git a/src/trace_processor/tables/slice_tables.h b/src/trace_processor/tables/slice_tables.h
index 7d75e7d..0500408 100644
--- a/src/trace_processor/tables/slice_tables.h
+++ b/src/trace_processor/tables/slice_tables.h
@@ -18,6 +18,7 @@
 #define SRC_TRACE_PROCESSOR_TABLES_SLICE_TABLES_H_
 
 #include "src/trace_processor/tables/macros.h"
+#include "src/trace_processor/tables/track_tables.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -28,12 +29,13 @@
   PERFETTO_TP_ROOT_TABLE(PARENT, C)                  \
   C(int64_t, ts, Column::Flag::kSorted)              \
   C(int64_t, dur)                                    \
-  C(uint32_t, track_id)                              \
+  C(TrackTable::Id, track_id)                        \
   C(StringPool::Id, category)                        \
   C(StringPool::Id, name)                            \
   C(uint32_t, depth)                                 \
   C(int64_t, stack_id)                               \
   C(int64_t, parent_stack_id)                        \
+  C(base::Optional<SliceTable::Id>, parent_id)       \
   C(uint32_t, arg_set_id)
 
 PERFETTO_TP_TABLE(PERFETTO_TP_SLICE_TABLE_DEF);
@@ -49,19 +51,63 @@
 
 PERFETTO_TP_TABLE(PERFETTO_TP_INSTANT_TABLE_DEF);
 
+#define PERFETTO_TP_SCHED_SLICE_TABLE_DEF(NAME, PARENT, C) \
+  NAME(SchedSliceTable, "sched_slice")                     \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                        \
+  C(int64_t, ts, Column::Flag::kSorted)                    \
+  C(int64_t, dur)                                          \
+  C(uint32_t, cpu)                                         \
+  C(uint32_t, utid)                                        \
+  C(StringPool::Id, end_state)                             \
+  C(int32_t, priority)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_SCHED_SLICE_TABLE_DEF);
+
 #define PERFETTO_TP_GPU_SLICES_DEF(NAME, PARENT, C) \
   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)
 
 PERFETTO_TP_TABLE(PERFETTO_TP_GPU_SLICES_DEF);
 
+#define PERFETTO_TP_GRAPHICS_FRAME_SLICES_DEF(NAME, PARENT, C) \
+  NAME(GraphicsFrameSliceTable, "frame_slice")                 \
+  PARENT(PERFETTO_TP_SLICE_TABLE_DEF, C)                       \
+  C(StringPool::Id, frame_numbers)                             \
+  C(StringPool::Id, layer_names)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_GRAPHICS_FRAME_SLICES_DEF);
+
+// frame_slice -> frame_stats : 1 -> Many,
+// with frame_slice.id = frame_stats.slice_id
+#define PERFETTO_TP_GRAPHICS_FRAME_STATS_DEF(NAME, PARENT, C) \
+  NAME(GraphicsFrameStatsTable, "frame_stats")                \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                           \
+  C(uint32_t, slice_id)                                       \
+  C(int64_t, queue_to_acquire_time)                           \
+  C(int64_t, acquire_to_latch_time)                           \
+  C(int64_t, latch_to_present_time)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_GRAPHICS_FRAME_STATS_DEF);
+
+#define PERFETTO_TP_DESCRIBE_SLICE_TABLE(NAME, PARENT, C) \
+  NAME(DescribeSliceTable, "describe_slice")              \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                       \
+  C(uint32_t, slice_id, Column::Flag::kHidden)            \
+  C(StringPool::Id, description)                          \
+  C(StringPool::Id, doc_link)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_DESCRIBE_SLICE_TABLE);
+
 }  // namespace tables
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/tables/table_destructors.cc b/src/trace_processor/tables/table_destructors.cc
new file mode 100644
index 0000000..fcfce00
--- /dev/null
+++ b/src/trace_processor/tables/table_destructors.cc
@@ -0,0 +1,84 @@
+/*
+ * 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/tables/android_tables.h"
+#include "src/trace_processor/tables/counter_tables.h"
+#include "src/trace_processor/tables/metadata_tables.h"
+#include "src/trace_processor/tables/profiler_tables.h"
+#include "src/trace_processor/tables/slice_tables.h"
+#include "src/trace_processor/tables/track_tables.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace macros_internal {
+// macros_internal.h
+MacroTable::~MacroTable() = default;
+}  // namespace macros_internal
+
+namespace tables {
+// android_tables.h
+AndroidLogTable::~AndroidLogTable() = default;
+
+// counter_tables.h
+CounterTable::~CounterTable() = default;
+
+// metadata_tables.h
+RawTable::~RawTable() = default;
+ArgTable::~ArgTable() = default;
+MetadataTable::~MetadataTable() = default;
+ThreadTable::~ThreadTable() = default;
+ProcessTable::~ProcessTable() = default;
+
+// profiler_tables.h
+StackProfileMappingTable::~StackProfileMappingTable() = default;
+StackProfileFrameTable::~StackProfileFrameTable() = default;
+StackProfileCallsiteTable::~StackProfileCallsiteTable() = default;
+CpuProfileStackSampleTable::~CpuProfileStackSampleTable() = default;
+SymbolTable::~SymbolTable() = default;
+HeapProfileAllocationTable::~HeapProfileAllocationTable() = default;
+ExperimentalFlamegraphNodesTable::~ExperimentalFlamegraphNodesTable() = default;
+HeapGraphObjectTable::~HeapGraphObjectTable() = default;
+HeapGraphClassTable::~HeapGraphClassTable() = default;
+HeapGraphReferenceTable::~HeapGraphReferenceTable() = default;
+VulkanMemoryAllocationsTable::~VulkanMemoryAllocationsTable() = default;
+PackageListTable::~PackageListTable() = default;
+ProfilerSmapsTable::~ProfilerSmapsTable() = default;
+
+// slice_tables.h
+SliceTable::~SliceTable() = default;
+InstantTable::~InstantTable() = default;
+SchedSliceTable::~SchedSliceTable() = default;
+GpuSliceTable::~GpuSliceTable() = default;
+GraphicsFrameSliceTable::~GraphicsFrameSliceTable() = default;
+GraphicsFrameStatsTable::~GraphicsFrameStatsTable() = default;
+DescribeSliceTable::~DescribeSliceTable() = default;
+
+// track_tables.h
+TrackTable::~TrackTable() = default;
+ProcessTrackTable::~ProcessTrackTable() = default;
+ThreadTrackTable::~ThreadTrackTable() = default;
+GpuTrackTable::~GpuTrackTable() = default;
+CounterTrackTable::~CounterTrackTable() = default;
+ThreadCounterTrackTable::~ThreadCounterTrackTable() = default;
+ProcessCounterTrackTable::~ProcessCounterTrackTable() = default;
+CpuCounterTrackTable::~CpuCounterTrackTable() = default;
+IrqCounterTrackTable::~IrqCounterTrackTable() = default;
+SoftirqCounterTrackTable::~SoftirqCounterTrackTable() = default;
+GpuCounterTrackTable::~GpuCounterTrackTable() = default;
+}  // namespace tables
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/thread_table_unittest.cc b/src/trace_processor/thread_table_unittest.cc
deleted file mode 100644
index fda86e1..0000000
--- a/src/trace_processor/thread_table_unittest.cc
+++ /dev/null
@@ -1,185 +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 "src/trace_processor/args_tracker.h"
-#include "src/trace_processor/event_tracker.h"
-#include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
-#include "src/trace_processor/process_table.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 ThreadTableUnittest : public ::testing::Test {
- public:
-  ThreadTableUnittest() {
-    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_.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_));
-    sched_tracker_ = SchedEventTracker::GetOrCreate(&context_);
-
-    ThreadTable::RegisterTable(db_.get(), context_.storage.get());
-    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_;
-  SchedEventTracker* sched_tracker_;
-};
-
-TEST_F(ThreadTableUnittest, Select) {
-  uint32_t cpu = 3;
-  int64_t timestamp = 100;
-  uint32_t prev_state = 32;
-  static const char kThreadName1[] = "thread1";
-  static const char kThreadName2[] = "thread2";
-  int32_t prio = 1024;
-
-  sched_tracker_->PushSchedSwitch(cpu, timestamp, /*tid=*/1, kThreadName2, prio,
-                                  prev_state,
-                                  /*tid=*/4, kThreadName1, prio);
-  sched_tracker_->PushSchedSwitch(cpu, timestamp + 1, /*tid=*/4, kThreadName1,
-                                  prio, prev_state,
-                                  /*tid=*/1, kThreadName2, prio);
-
-  context_.process_tracker->SetProcessMetadata(2, base::nullopt, "test");
-  context_.process_tracker->UpdateThread(4 /*tid*/, 2 /*pid*/);
-  PrepareValidStatement("SELECT utid, upid, tid, name FROM thread where tid=4");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 0), 1 /* utid */);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 1), 1 /* upid */);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 2), 4 /* tid */);
-  ASSERT_STREQ(GetColumnAsText(3), kThreadName1);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(ThreadTableUnittest, SelectWhere) {
-  uint32_t cpu = 3;
-  int64_t timestamp = 100;
-  uint32_t prev_state = 32;
-  static const char kThreadName1[] = "thread1";
-  static const char kThreadName2[] = "thread2";
-  int32_t prio = 1024;
-
-  sched_tracker_->PushSchedSwitch(cpu, timestamp, /*tid=*/1, kThreadName2, prio,
-                                  prev_state,
-                                  /*tid=*/4, kThreadName1, prio);
-  sched_tracker_->PushSchedSwitch(cpu, timestamp + 1, /*tid=*/4, kThreadName1,
-                                  prio, prev_state,
-                                  /*tid=*/1, kThreadName2, prio);
-  sched_tracker_->PushSchedSwitch(cpu, timestamp + 2, /*tid=*/1, kThreadName2,
-                                  prio, prev_state,
-                                  /*tid=*/4, kThreadName1, prio);
-
-  context_.process_tracker->SetProcessMetadata(2, base::nullopt, "test");
-  context_.process_tracker->UpdateThread(4 /*tid*/, 2 /*pid*/);
-  context_.process_tracker->UpdateThread(1 /*tid*/, 2 /*pid*/);
-  PrepareValidStatement(
-      "SELECT utid, upid, tid, name FROM thread where tid = 4");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 0), 1 /* utid */);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 1), 1 /* upid */);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 2), 4 /* tid */);
-  ASSERT_STREQ(GetColumnAsText(3), kThreadName1);
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(ThreadTableUnittest, JoinWithProcess) {
-  uint32_t cpu = 3;
-  int64_t timestamp = 100;
-  uint32_t prev_state = 32;
-  static const char kThreadName1[] = "thread1";
-  static const char kThreadName2[] = "thread2";
-  int32_t prio = 1024;
-
-  sched_tracker_->PushSchedSwitch(cpu, timestamp, /*tid=*/1, kThreadName2, prio,
-                                  prev_state,
-                                  /*tid=*/4, kThreadName1, prio);
-  sched_tracker_->PushSchedSwitch(cpu, timestamp + 1, /*tid=*/4, kThreadName1,
-                                  prio, prev_state,
-                                  /*tid=*/1, kThreadName2, prio);
-
-  // Also create a process for which we haven't seen any thread.
-  context_.process_tracker->SetProcessMetadata(7, base::nullopt, "pid7");
-
-  context_.process_tracker->SetProcessMetadata(2, base::nullopt, "pid2");
-  context_.process_tracker->UpdateThread(/*tid=*/4, /*pid=*/2);
-
-  PrepareValidStatement(
-      "SELECT utid, thread.tid, thread.name, process.upid, process.pid, "
-      "process.name FROM thread INNER JOIN process USING (upid) WHERE pid = 2 "
-      "ORDER BY tid");
-
-  // At this point we should see two threads bound to process with pid=2:
-
-  // (1) The (implicitly created) main thread (tid=pid=2).
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  auto utid_for_tid2 = sqlite3_column_int(*stmt_, 0);
-  ASSERT_GT(utid_for_tid2, 0);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 1), 2 /* tid */);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 4), 2 /* pid */);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 3), 2 /* upid */);
-  ASSERT_EQ(GetColumnAsText(2), nullptr);  // No name seen for main thread.
-  ASSERT_STREQ(GetColumnAsText(5), "pid2");
-
-  // (2) A thread with tid=4.
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  auto utid_for_tid4 = sqlite3_column_int(*stmt_, 0);
-  ASSERT_GT(utid_for_tid4, 0);
-  ASSERT_NE(utid_for_tid4, utid_for_tid2);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 1), 4 /* tid */);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 4), 2 /* pid */);
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 3), 2 /* upid */);
-  ASSERT_STREQ(GetColumnAsText(2), kThreadName1);
-  ASSERT_STREQ(GetColumnAsText(5), "pid2");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-}  // namespace
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/timestamped_trace_piece.h b/src/trace_processor/timestamped_trace_piece.h
index 2414f89..74b484f 100644
--- a/src/trace_processor/timestamped_trace_piece.h
+++ b/src/trace_processor/timestamped_trace_piece.h
@@ -20,19 +20,12 @@
 #include "perfetto/base/build_config.h"
 #include "perfetto/trace_processor/basic_types.h"
 #include "src/trace_processor/importers/fuchsia/fuchsia_record.h"
+#include "src/trace_processor/importers/json/json_utils.h"
 #include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/importers/systrace/systrace_line.h"
+#include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/trace_blob_view.h"
-#include "src/trace_processor/trace_processor_context.h"
-#include "src/trace_processor/trace_storage.h"
-
-#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON_IMPORT)
-#include <json/value.h>
-#else   // PERFETTO_BUILDFLAG(PERFETTO_TP_JSON_IMPORT)
-// Json traces are only supported in some build configurations (standalone, UI).
-namespace Json {
-class Value {};
-}  // namespace Json
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_TP_JSON_IMPORT)
+#include "src/trace_processor/types/trace_processor_context.h"
 
 // GCC can't figure out the relationship between TimestampedTracePiece's type
 // and the union, and thus thinks that we may be moving or destroying
@@ -67,16 +60,15 @@
 };
 
 struct TrackEventData : public TracePacketData {
-  TrackEventData(TraceBlobView pv,
-                 PacketSequenceStateGeneration* generation,
-                 int64_t thread_ts,
-                 int64_t thread_ic)
-      : TracePacketData{std::move(pv), generation},
-        thread_timestamp(thread_ts),
-        thread_instruction_count(thread_ic) {}
+  TrackEventData(TraceBlobView pv, PacketSequenceStateGeneration* generation)
+      : TracePacketData{std::move(pv), generation} {}
 
-  int64_t thread_timestamp;
-  int64_t thread_instruction_count;
+  static constexpr size_t kMaxNumExtraCounters = 8;
+
+  int64_t thread_timestamp = 0;
+  int64_t thread_instruction_count = 0;
+  int64_t counter_value = 0;
+  std::array<int64_t, kMaxNumExtraCounters> extra_counter_values = {};
 };
 
 // A TimestampedTracePiece is (usually a reference to) a piece of a trace that
@@ -90,7 +82,8 @@
     kInlineSchedWaking,
     kJsonValue,
     kFuchsiaRecord,
-    kTrackEvent
+    kTrackEvent,
+    kSystraceLine,
   };
 
   TimestampedTracePiece(int64_t ts,
@@ -132,6 +125,14 @@
         packet_idx(idx),
         type(Type::kTrackEvent) {}
 
+  TimestampedTracePiece(int64_t ts,
+                        uint64_t idx,
+                        std::unique_ptr<SystraceLine> ted)
+      : systrace_line(std::move(ted)),
+        timestamp(ts),
+        packet_idx(idx),
+        type(Type::kSystraceLine) {}
+
   TimestampedTracePiece(int64_t ts, uint64_t idx, InlineSchedSwitch iss)
       : sched_switch(std::move(iss)),
         timestamp(ts),
@@ -175,6 +176,9 @@
         new (&track_event_data)
             std::unique_ptr<TrackEventData>(std::move(ttp.track_event_data));
         break;
+      case Type::kSystraceLine:
+        new (&systrace_line)
+            std::unique_ptr<SystraceLine>(std::move(ttp.systrace_line));
     }
     timestamp = ttp.timestamp;
     packet_idx = ttp.packet_idx;
@@ -194,6 +198,9 @@
     return *this;
   }
 
+  TimestampedTracePiece(const TimestampedTracePiece&) = delete;
+  TimestampedTracePiece& operator=(const TimestampedTracePiece&) = delete;
+
   ~TimestampedTracePiece() {
     switch (type) {
       case Type::kInvalid:
@@ -215,6 +222,9 @@
       case Type::kTrackEvent:
         track_event_data.~unique_ptr();
         break;
+      case Type::kSystraceLine:
+        systrace_line.~unique_ptr();
+        break;
     }
   }
 
@@ -240,6 +250,7 @@
     std::unique_ptr<Json::Value> json_value;
     std::unique_ptr<FuchsiaRecord> fuchsia_record;
     std::unique_ptr<TrackEventData> track_event_data;
+    std::unique_ptr<SystraceLine> systrace_line;
   };
 
   int64_t timestamp;
diff --git a/src/trace_processor/trace_database_integrationtest.cc b/src/trace_processor/trace_database_integrationtest.cc
index a6ef050..52c9a61 100644
--- a/src/trace_processor/trace_database_integrationtest.cc
+++ b/src/trace_processor/trace_database_integrationtest.cc
@@ -23,7 +23,6 @@
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/trace_processor/trace_processor.h"
 #include "src/base/test/utils.h"
-#include "src/trace_processor/importers/json/json_trace_parser.h"
 #include "test/gtest_and_gmock.h"
 
 namespace perfetto {
@@ -90,6 +89,21 @@
   ASSERT_FALSE(it.Next());
 }
 
+// Tests that the duration of the last slice is accounted in the computation
+// of the trace boundaries. Linux ftraces tend to hide this problem because
+// after the last sched_switch there's always a "wake" event which causes the
+// raw table to fix the bounds.
+TEST_F(TraceProcessorIntegrationTest, TraceBoundsUserspaceOnly) {
+  ASSERT_TRUE(LoadTrace("sfgate.json").ok());
+  auto it = Query("select start_ts, end_ts from trace_bounds");
+  ASSERT_TRUE(it.Next());
+  ASSERT_EQ(it.Get(0).type, SqlValue::kLong);
+  ASSERT_EQ(it.Get(0).long_value, 2213649212614000);
+  ASSERT_EQ(it.Get(1).type, SqlValue::kLong);
+  ASSERT_EQ(it.Get(1).long_value, 2213689745140000);
+  ASSERT_FALSE(it.Next());
+}
+
 TEST_F(TraceProcessorIntegrationTest, Hash) {
   auto it = Query("select HASH()");
   ASSERT_TRUE(it.Next());
@@ -124,7 +138,7 @@
   ASSERT_TRUE(it.Get(0).is_null());
 }
 
-#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON_IMPORT)
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
 TEST_F(TraceProcessorIntegrationTest, Sfgate) {
   ASSERT_TRUE(LoadTrace("sfgate.json", strlen("{\"traceEvents\":[")).ok());
   auto it = Query(
@@ -164,7 +178,7 @@
 TEST_F(TraceProcessorIntegrationTest, DISABLED_Clusterfuzz14357) {
   ASSERT_FALSE(LoadTrace("clusterfuzz_14357", 4096).ok());
 }
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_TP_JSON_IMPORT)
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
 
 TEST_F(TraceProcessorIntegrationTest, Clusterfuzz14730) {
   ASSERT_TRUE(LoadTrace("clusterfuzz_14730", 4096).ok());
@@ -174,7 +188,6 @@
   ASSERT_TRUE(LoadTrace("clusterfuzz_14753", 4096).ok());
 }
 
-#if PERFETTO_BUILDFLAG(PERFETTO_TP_FUCHSIA)
 TEST_F(TraceProcessorIntegrationTest, Clusterfuzz14762) {
   ASSERT_TRUE(LoadTrace("clusterfuzz_14762", 4096 * 1024).ok());
   auto it = Query("select sum(value) from stats where severity = 'error';");
@@ -195,14 +208,15 @@
   ASSERT_TRUE(it.Next());
   ASSERT_GT(it.Get(0).long_value, 0);
 }
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_TP_FUCHSIA)
 
 TEST_F(TraceProcessorIntegrationTest, Clusterfuzz15252) {
   ASSERT_TRUE(LoadTrace("clusterfuzz_15252", 4096).ok());
 }
 
 TEST_F(TraceProcessorIntegrationTest, Clusterfuzz17805) {
-  ASSERT_TRUE(LoadTrace("clusterfuzz_17805", 4096).ok());
+  // This trace fails to load as it's detected as a systrace but is full of
+  // garbage data.
+  ASSERT_TRUE(!LoadTrace("clusterfuzz_17805", 4096).ok());
 }
 
 TEST_F(TraceProcessorIntegrationTest, RestoreInitialTables) {
@@ -227,6 +241,42 @@
   }
 }
 
+// This test checks that a ninja trace is tokenized properly even if read in
+// small chunks of 1KB each. The values used in the test have been cross-checked
+// with opening the same trace with ninjatracing + chrome://tracing.
+TEST_F(TraceProcessorIntegrationTest, NinjaLog) {
+  ASSERT_TRUE(LoadTrace("ninja_log", 1024).ok());
+  auto it = Query("select count(*) from process where name like 'build';");
+  ASSERT_TRUE(it.Next());
+  ASSERT_EQ(it.Get(0).long_value, 2);
+
+  it = Query(
+      "select count(*) from thread left join process using(upid) where "
+      "thread.name like 'worker%' and process.pid=1");
+  ASSERT_TRUE(it.Next());
+  ASSERT_EQ(it.Get(0).long_value, 14);
+
+  it = Query(
+      "create view slices_1st_build as select slices.* from slices left "
+      "join thread_track on(slices.track_id == thread_track.id) left join "
+      "thread using(utid) left join process using(upid) where pid=2");
+  it.Next();
+  ASSERT_TRUE(it.Status().ok());
+
+  it = Query("select (max(ts) - min(ts)) / 1000000 from slices_1st_build");
+  ASSERT_TRUE(it.Next());
+  ASSERT_EQ(it.Get(0).long_value, 12612);
+
+  it = Query("select name from slices_1st_build order by ts desc limit 1");
+  ASSERT_TRUE(it.Next());
+  ASSERT_STREQ(it.Get(0).string_value,
+               "obj/src/trace_processor/unittests.trace_sorter_unittest.o");
+
+  it = Query("select sum(dur) / 1000000 from slices_1st_build");
+  ASSERT_TRUE(it.Next());
+  ASSERT_EQ(it.Get(0).long_value, 276174);
+}
+
 }  // namespace
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/trace_processor_context.cc b/src/trace_processor/trace_processor_context.cc
index bb5b6a0..337a4d6 100644
--- a/src/trace_processor/trace_processor_context.cc
+++ b/src/trace_processor/trace_processor_context.cc
@@ -14,24 +14,27 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
-#include "src/trace_processor/args_tracker.h"
 #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/forwarding_trace_parser.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/clock_tracker.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/global_args_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
 #include "src/trace_processor/importers/ftrace/ftrace_module.h"
-#include "src/trace_processor/importers/json/json_trace_parser.h"
+#include "src/trace_processor/importers/proto/heap_profile_tracker.h"
+#include "src/trace_processor/importers/proto/metadata_tracker.h"
+#include "src/trace_processor/importers/proto/perf_sample_tracker.h"
+#include "src/trace_processor/importers/proto/proto_importer_module.h"
 #include "src/trace_processor/importers/proto/proto_trace_parser.h"
+#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
 #include "src/trace_processor/importers/proto/track_event_module.h"
-#include "src/trace_processor/metadata_tracker.h"
-#include "src/trace_processor/process_tracker.h"
-#include "src/trace_processor/slice_tracker.h"
-#include "src/trace_processor/stack_profile_tracker.h"
 #include "src/trace_processor/trace_sorter.h"
-#include "src/trace_processor/track_tracker.h"
+#include "src/trace_processor/types/destructible.h"
 
 namespace perfetto {
 namespace trace_processor {
diff --git a/src/trace_processor/trace_processor_context.h b/src/trace_processor/trace_processor_context.h
deleted file mode 100644
index 63e0886..0000000
--- a/src/trace_processor/trace_processor_context.h
+++ /dev/null
@@ -1,97 +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_TRACE_PROCESSOR_CONTEXT_H_
-#define SRC_TRACE_PROCESSOR_TRACE_PROCESSOR_CONTEXT_H_
-
-#include <memory>
-#include <vector>
-
-#include "perfetto/trace_processor/basic_types.h"
-#include "src/trace_processor/chunked_trace_reader.h"
-#include "src/trace_processor/destructible.h"
-#include "src/trace_processor/importers/proto/proto_importer_module.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-class ArgsTracker;
-class ChunkedTraceReader;
-class ClockTracker;
-class EventTracker;
-class FtraceModule;
-class GlobalArgsTracker;
-class HeapGraphTracker;
-class HeapProfileTracker;
-class MetadataTracker;
-class ProcessTracker;
-class SliceTracker;
-class TraceParser;
-class TraceSorter;
-class TraceStorage;
-class TrackTracker;
-
-class TraceProcessorContext {
- public:
-  TraceProcessorContext();
-  ~TraceProcessorContext();
-
-  Config config;
-
-  std::unique_ptr<TraceStorage> storage;
-  std::unique_ptr<TrackTracker> track_tracker;
-  std::unique_ptr<SliceTracker> slice_tracker;
-  std::unique_ptr<ProcessTracker> process_tracker;
-  std::unique_ptr<EventTracker> event_tracker;
-  std::unique_ptr<ClockTracker> clock_tracker;
-  std::unique_ptr<TraceParser> parser;
-  std::unique_ptr<TraceSorter> sorter;
-  std::unique_ptr<ChunkedTraceReader> chunk_reader;
-  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> 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
-  // (storage_full target). The corresponding initialization happens in
-  // register_additional_modules.cc.
-  std::unique_ptr<ChunkedTraceReader> systrace_trace_parser;
-
-  // The module at the index N is registered to handle field id N in
-  // TracePacket.
-  std::vector<ProtoImporterModule*> modules_by_field;
-  std::vector<std::unique_ptr<ProtoImporterModule>> modules;
-  FtraceModule* ftrace_module = nullptr;
-};
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_TRACE_PROCESSOR_CONTEXT_H_
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 154dd3e..95a3bc8 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -23,29 +23,34 @@
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/ext/base/string_utils.h"
+#include "src/trace_processor/dynamic/describe_slice_generator.h"
+#include "src/trace_processor/dynamic/experimental_counter_dur_generator.h"
+#include "src/trace_processor/dynamic/experimental_flamegraph_generator.h"
+#include "src/trace_processor/dynamic/experimental_slice_layout_generator.h"
+#include "src/trace_processor/export_json.h"
+#include "src/trace_processor/importers/additional_modules.h"
 #include "src/trace_processor/importers/ftrace/sched_event_tracker.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"
-#include "src/trace_processor/sql_stats_table.h"
-#include "src/trace_processor/sqlite/db_sqlite_table.h"
+#include "src/trace_processor/importers/fuchsia/fuchsia_trace_parser.h"
+#include "src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h"
+#include "src/trace_processor/importers/gzip/gzip_trace_parser.h"
+#include "src/trace_processor/importers/json/json_trace_parser.h"
+#include "src/trace_processor/importers/json/json_trace_tokenizer.h"
+#include "src/trace_processor/importers/proto/metadata_tracker.h"
+#include "src/trace_processor/importers/systrace/systrace_trace_parser.h"
+#include "src/trace_processor/sqlite/span_join_operator_table.h"
+#include "src/trace_processor/sqlite/sql_stats_table.h"
 #include "src/trace_processor/sqlite/sqlite3_str_split.h"
+#include "src/trace_processor/sqlite/sqlite_raw_table.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/sqlite/sqlite_utils.h"
+#include "src/trace_processor/sqlite/stats_table.h"
+#include "src/trace_processor/sqlite/window_operator_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"
 #include "src/trace_processor/metrics/metrics.h"
 #include "src/trace_processor/metrics/sql_metrics.h"
 
-#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
-#include "src/trace_processor/export_json.h"
-#endif
-
 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 #include <cxxabi.h>
 #endif
@@ -185,6 +190,20 @@
                "0.0 as value "
                "FROM instant;",
                0, 0, &error);
+
+  if (error) {
+    PERFETTO_ELOG("Error initializing: %s", error);
+    sqlite3_free(error);
+  }
+
+  sqlite3_exec(db,
+               "CREATE VIEW sched AS "
+               "SELECT "
+               "*, "
+               "ts + dur as ts_end "
+               "FROM sched_slice;",
+               0, 0, &error);
+
   if (error) {
     PERFETTO_ELOG("Error initializing: %s", error);
     sqlite3_free(error);
@@ -226,7 +245,6 @@
   }
 }
 
-#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
 void ExportJson(sqlite3_context* ctx, int /*argc*/, sqlite3_value** argv) {
   TraceStorage* storage = static_cast<TraceStorage*>(sqlite3_user_data(ctx));
   FILE* output;
@@ -262,7 +280,6 @@
     PERFETTO_ELOG("Error initializing EXPORT_JSON");
   }
 }
-#endif
 
 void Hash(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
   base::Hash hash;
@@ -312,6 +329,54 @@
 #endif
 }
 
+void LastNonNullStep(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
+  if (argc != 1) {
+    sqlite3_result_error(
+        ctx, "Unsupported number of args passed to LAST_NON_NULL", -1);
+    return;
+  }
+  sqlite3_value* value = argv[0];
+  if (sqlite3_value_type(value) == SQLITE_NULL) {
+    return;
+  }
+  sqlite3_value** ptr = reinterpret_cast<sqlite3_value**>(
+      sqlite3_aggregate_context(ctx, sizeof(sqlite3_value*)));
+  if (ptr) {
+    if (*ptr != nullptr) {
+      sqlite3_value_free(*ptr);
+    }
+    *ptr = sqlite3_value_dup(value);
+  }
+}
+
+void LastNonNullInverse(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
+  // Do nothing.
+  base::ignore_result(ctx);
+  base::ignore_result(argc);
+  base::ignore_result(argv);
+}
+
+void LastNonNullValue(sqlite3_context* ctx) {
+  sqlite3_value** ptr =
+      reinterpret_cast<sqlite3_value**>(sqlite3_aggregate_context(ctx, 0));
+  if (!ptr || !*ptr) {
+    sqlite3_result_null(ctx);
+  } else {
+    sqlite3_result_value(ctx, *ptr);
+  }
+}
+
+void LastNonNullFinal(sqlite3_context* ctx) {
+  sqlite3_value** ptr =
+      reinterpret_cast<sqlite3_value**>(sqlite3_aggregate_context(ctx, 0));
+  if (!ptr || !*ptr) {
+    sqlite3_result_null(ctx);
+  } else {
+    sqlite3_result_value(ctx, *ptr);
+    sqlite3_value_free(*ptr);
+  }
+}
+
 void CreateHashFunction(sqlite3* db) {
   auto ret = sqlite3_create_function_v2(
       db, "HASH", -1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr, &Hash,
@@ -330,6 +395,77 @@
   }
 }
 
+void CreateLastNonNullFunction(sqlite3* db) {
+  auto ret = sqlite3_create_window_function(
+      db, "LAST_NON_NULL", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr,
+      &LastNonNullStep, &LastNonNullFinal, &LastNonNullValue,
+      &LastNonNullInverse, nullptr);
+  if (ret) {
+    PERFETTO_ELOG("Error initializing LAST_NON_NULL");
+  }
+}
+
+void ExtractArg(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
+  if (argc != 2) {
+    sqlite3_result_error(ctx, "EXTRACT_ARG: 2 args required", -1);
+    return;
+  }
+  if (sqlite3_value_type(argv[0]) != SQLITE_INTEGER) {
+    sqlite3_result_error(ctx, "EXTRACT_ARG: 1st argument should be arg set id",
+                         -1);
+    return;
+  }
+  if (sqlite3_value_type(argv[1]) != SQLITE_TEXT) {
+    sqlite3_result_error(ctx, "EXTRACT_ARG: 2nd argument should be key", -1);
+    return;
+  }
+
+  TraceStorage* storage = static_cast<TraceStorage*>(sqlite3_user_data(ctx));
+  uint32_t arg_set_id = static_cast<uint32_t>(sqlite3_value_int(argv[0]));
+  const char* key = reinterpret_cast<const char*>(sqlite3_value_text(argv[1]));
+
+  const auto& args = storage->arg_table();
+  RowMap filtered = args.FilterToRowMap(
+      {args.arg_set_id().eq(arg_set_id), args.key().eq(key)});
+  if (filtered.size() == 0) {
+    sqlite3_result_null(ctx);
+    return;
+  }
+  if (filtered.size() > 1) {
+    sqlite3_result_error(
+        ctx, "EXTRACT_ARG: received multiple args matching arg set id and key",
+        -1);
+  }
+
+  uint32_t idx = filtered.Get(0);
+  Variadic::Type type = *storage->GetVariadicTypeForId(args.value_type()[idx]);
+  switch (type) {
+    case Variadic::kBool:
+    case Variadic::kInt:
+    case Variadic::kUint:
+    case Variadic::kPointer:
+      sqlite3_result_int64(ctx, *args.int_value()[idx]);
+      break;
+    case Variadic::kJson:
+    case Variadic::kString:
+      sqlite3_result_text(ctx, args.string_value().GetString(idx).data(), -1,
+                          nullptr);
+      break;
+    case Variadic::kReal:
+      sqlite3_result_double(ctx, *args.real_value()[idx]);
+      break;
+  }
+}
+
+void CreateExtractArgFunction(TraceStorage* ts, sqlite3* db) {
+  auto ret = sqlite3_create_function_v2(db, "EXTRACT_ARG", 2,
+                                        SQLITE_UTF8 | SQLITE_DETERMINISTIC, ts,
+                                        &ExtractArg, nullptr, nullptr, nullptr);
+  if (ret != SQLITE_OK) {
+    PERFETTO_FATAL("Error initializing EXTRACT_ARG: %s", sqlite3_errmsg(db));
+  }
+}
+
 void SetupMetrics(TraceProcessor* tp,
                   sqlite3* db,
                   std::vector<metrics::SqlMetricFile>* sql_metrics) {
@@ -360,30 +496,55 @@
       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) {
+  context_.fuchsia_trace_tokenizer.reset(new FuchsiaTraceTokenizer(&context_));
+  context_.fuchsia_trace_parser.reset(new FuchsiaTraceParser(&context_));
+
+  context_.systrace_trace_parser.reset(new SystraceTraceParser(&context_));
+
+  if (gzip::IsGzipSupported())
+    context_.gzip_trace_parser.reset(new GzipTraceParser(&context_));
+
+  if (json::IsJsonSupported()) {
+    context_.json_trace_tokenizer.reset(new JsonTraceTokenizer(&context_));
+    context_.json_trace_parser.reset(new JsonTraceParser(&context_));
+  }
+
   RegisterAdditionalModules(&context_);
+
   sqlite3* db = nullptr;
-  PERFETTO_CHECK(sqlite3_initialize() == SQLITE_OK);
+  EnsureSqliteInitialized();
   PERFETTO_CHECK(sqlite3_open(":memory:", &db) == SQLITE_OK);
   InitializeSqlite(db);
   CreateBuiltinTables(db);
   CreateBuiltinViews(db);
   db_.reset(std::move(db));
 
-#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
-  CreateJsonExportFunction(this->context_.storage.get(), db);
-#endif
+  CreateJsonExportFunction(context_.storage.get(), db);
   CreateHashFunction(db);
   CreateDemangledNameFunction(db);
+  CreateLastNonNullFunction(db);
+  CreateExtractArgFunction(context_.storage.get(), db);
 
   SetupMetrics(this, *db_, &sql_metrics_);
 
+  // Setup the query cache.
+  query_cache_.reset(new QueryCache());
+
   const TraceStorage* storage = context_.storage.get();
 
-  SchedSliceTable::RegisterTable(*db_, storage);
   SqlStatsTable::RegisterTable(*db_, storage);
   StatsTable::RegisterTable(*db_, storage);
 
@@ -392,87 +553,67 @@
   WindowOperatorTable::RegisterTable(*db_, storage);
 
   // New style tables but with some custom logic.
-  SqliteExperimentalFlamegraphTable::RegisterTable(*db_, &context_);
-  SqliteRawTable::RegisterTable(*db_, context_.storage.get());
+  SqliteRawTable::RegisterTable(*db_, query_cache_.get(),
+                                context_.storage.get());
+
+  // Tables dynamically generated at query time.
+  RegisterDynamicTable(std::unique_ptr<ExperimentalFlamegraphGenerator>(
+      new ExperimentalFlamegraphGenerator(&context_)));
+  RegisterDynamicTable(std::unique_ptr<ExperimentalCounterDurGenerator>(
+      new ExperimentalCounterDurGenerator(storage->counter_table())));
+  RegisterDynamicTable(std::unique_ptr<DescribeSliceGenerator>(
+      new DescribeSliceGenerator(&context_)));
+  RegisterDynamicTable(std::unique_ptr<ExperimentalSliceLayoutGenerator>(
+      new ExperimentalSliceLayoutGenerator(
+          context_.storage.get()->mutable_string_pool(),
+          &storage->slice_table())));
 
   // New style db-backed tables.
-  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());
+  RegisterDbTable(storage->arg_table());
+  RegisterDbTable(storage->thread_table());
+  RegisterDbTable(storage->process_table());
 
-  DbSqliteTable::RegisterTable(*db_, &storage->slice_table(),
-                               storage->slice_table().table_name());
-  DbSqliteTable::RegisterTable(*db_, &storage->instant_table(),
-                               storage->instant_table().table_name());
-  DbSqliteTable::RegisterTable(*db_, &storage->gpu_slice_table(),
-                               storage->gpu_slice_table().table_name());
+  RegisterDbTable(storage->slice_table());
+  RegisterDbTable(storage->sched_slice_table());
+  RegisterDbTable(storage->instant_table());
+  RegisterDbTable(storage->gpu_slice_table());
 
-  DbSqliteTable::RegisterTable(*db_, &storage->track_table(),
-                               storage->track_table().table_name());
-  DbSqliteTable::RegisterTable(*db_, &storage->thread_track_table(),
-                               storage->thread_track_table().table_name());
-  DbSqliteTable::RegisterTable(*db_, &storage->process_track_table(),
-                               storage->process_track_table().table_name());
-  DbSqliteTable::RegisterTable(*db_, &storage->gpu_track_table(),
-                               storage->gpu_track_table().table_name());
+  RegisterDbTable(storage->track_table());
+  RegisterDbTable(storage->thread_track_table());
+  RegisterDbTable(storage->process_track_table());
+  RegisterDbTable(storage->gpu_track_table());
 
-  DbSqliteTable::RegisterTable(*db_, &storage->counter_table(),
-                               storage->counter_table().table_name());
+  RegisterDbTable(storage->counter_table());
 
-  DbSqliteTable::RegisterTable(*db_, &storage->counter_track_table(),
-                               storage->counter_track_table().table_name());
-  DbSqliteTable::RegisterTable(
-      *db_, &storage->process_counter_track_table(),
-      storage->process_counter_track_table().table_name());
-  DbSqliteTable::RegisterTable(
-      *db_, &storage->thread_counter_track_table(),
-      storage->thread_counter_track_table().table_name());
-  DbSqliteTable::RegisterTable(*db_, &storage->cpu_counter_track_table(),
-                               storage->cpu_counter_track_table().table_name());
-  DbSqliteTable::RegisterTable(*db_, &storage->irq_counter_track_table(),
-                               storage->irq_counter_track_table().table_name());
-  DbSqliteTable::RegisterTable(
-      *db_, &storage->softirq_counter_track_table(),
-      storage->softirq_counter_track_table().table_name());
-  DbSqliteTable::RegisterTable(*db_, &storage->gpu_counter_track_table(),
-                               storage->gpu_counter_track_table().table_name());
+  RegisterDbTable(storage->counter_track_table());
+  RegisterDbTable(storage->process_counter_track_table());
+  RegisterDbTable(storage->thread_counter_track_table());
+  RegisterDbTable(storage->cpu_counter_track_table());
+  RegisterDbTable(storage->irq_counter_track_table());
+  RegisterDbTable(storage->softirq_counter_track_table());
+  RegisterDbTable(storage->gpu_counter_track_table());
 
-  DbSqliteTable::RegisterTable(*db_, &storage->heap_graph_object_table(),
-                               storage->heap_graph_object_table().table_name());
-  DbSqliteTable::RegisterTable(
-      *db_, &storage->heap_graph_reference_table(),
-      storage->heap_graph_reference_table().table_name());
+  RegisterDbTable(storage->heap_graph_object_table());
+  RegisterDbTable(storage->heap_graph_reference_table());
+  RegisterDbTable(storage->heap_graph_class_table());
 
-  DbSqliteTable::RegisterTable(*db_, &storage->symbol_table(),
-                               storage->symbol_table().table_name());
-  DbSqliteTable::RegisterTable(
-      *db_, &storage->heap_profile_allocation_table(),
-      storage->heap_profile_allocation_table().table_name());
-  DbSqliteTable::RegisterTable(
-      *db_, &storage->cpu_profile_stack_sample_table(),
-      storage->cpu_profile_stack_sample_table().table_name());
-  DbSqliteTable::RegisterTable(
-      *db_, &storage->stack_profile_callsite_table(),
-      storage->stack_profile_callsite_table().table_name());
-  DbSqliteTable::RegisterTable(
-      *db_, &storage->stack_profile_mapping_table(),
-      storage->stack_profile_mapping_table().table_name());
-  DbSqliteTable::RegisterTable(
-      *db_, &storage->stack_profile_frame_table(),
-      storage->stack_profile_frame_table().table_name());
+  RegisterDbTable(storage->symbol_table());
+  RegisterDbTable(storage->heap_profile_allocation_table());
+  RegisterDbTable(storage->cpu_profile_stack_sample_table());
+  RegisterDbTable(storage->stack_profile_callsite_table());
+  RegisterDbTable(storage->stack_profile_mapping_table());
+  RegisterDbTable(storage->stack_profile_frame_table());
+  RegisterDbTable(storage->package_list_table());
+  RegisterDbTable(storage->profiler_smaps_table());
 
-  DbSqliteTable::RegisterTable(*db_, &storage->android_log_table(),
-                               storage->android_log_table().table_name());
+  RegisterDbTable(storage->android_log_table());
 
-  DbSqliteTable::RegisterTable(
-      *db_, &storage->vulkan_memory_allocations_table(),
-      storage->vulkan_memory_allocations_table().table_name());
+  RegisterDbTable(storage->vulkan_memory_allocations_table());
 
-  DbSqliteTable::RegisterTable(*db_, &storage->metadata_table(),
-                               storage->metadata_table().table_name());
+  RegisterDbTable(storage->graphics_frame_slice_table());
+  RegisterDbTable(storage->graphics_frame_stats_table());
+
+  RegisterDbTable(storage->metadata_table());
 }
 
 TraceProcessorImpl::~TraceProcessorImpl() {
diff --git a/src/trace_processor/trace_processor_impl.h b/src/trace_processor/trace_processor_impl.h
index c747dfa..56dbdae 100644
--- a/src/trace_processor/trace_processor_impl.h
+++ b/src/trace_processor/trace_processor_impl.h
@@ -28,11 +28,13 @@
 #include "perfetto/trace_processor/basic_types.h"
 #include "perfetto/trace_processor/status.h"
 #include "perfetto/trace_processor/trace_processor.h"
+#include "src/trace_processor/sqlite/db_sqlite_table.h"
+#include "src/trace_processor/sqlite/query_cache.h"
 #include "src/trace_processor/sqlite/scoped_db.h"
 #include "src/trace_processor/trace_processor_storage_impl.h"
 
-#include "src/trace_processor/descriptors.h"
 #include "src/trace_processor/metrics/metrics.h"
+#include "src/trace_processor/util/descriptors.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -73,7 +75,20 @@
   // Needed for iterators to be able to delete themselves from the vector.
   friend class IteratorImpl;
 
+  template <typename Table>
+  void RegisterDbTable(const Table& table) {
+    DbSqliteTable::RegisterTable(*db_, query_cache_.get(), Table::Schema(),
+                                 &table, table.table_name());
+  }
+
+  void RegisterDynamicTable(
+      std::unique_ptr<DbSqliteTable::DynamicTableGenerator> generator) {
+    DbSqliteTable::RegisterTable(*db_, query_cache_.get(),
+                                 std::move(generator));
+  }
+
   ScopedDb db_;
+  std::unique_ptr<QueryCache> query_cache_;
 
   DescriptorPool pool_;
   std::vector<metrics::SqlMetricFile> sql_metrics_;
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index 594e13e..b6fefe2 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 #include <fcntl.h>
 #include <inttypes.h>
 #include <stdio.h>
@@ -34,11 +33,13 @@
 #include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/ext/base/string_utils.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"
+#include "src/trace_processor/util/proto_to_json.h"
+#include "src/trace_processor/util/status_macros.h"
 
 #if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD)
 #include "src/trace_processor/rpc/httpd.h"
@@ -168,7 +169,7 @@
 
 #endif  // PERFETTO_TP_LINENOISE
 
-bool PrintStats() {
+util::Status PrintStats() {
   auto it = g_tp->ExecuteQuery(
       "SELECT name, idx, source, value from stats "
       "where severity IN ('error', 'data_loss') and value > 0");
@@ -215,20 +216,18 @@
 
   util::Status status = it.Status();
   if (!status.ok()) {
-    PERFETTO_ELOG("Error while iterating stats %s", status.c_message());
-    return false;
+    return util::ErrStatus("Error while iterating stats (%s)",
+                           status.c_message());
   }
-  return true;
+  return util::OkStatus();
 }
 
-int ExportTraceToDatabase(const std::string& output_name) {
+util::Status ExportTraceToDatabase(const std::string& output_name) {
   PERFETTO_CHECK(output_name.find("'") == std::string::npos);
   {
     base::ScopedFile fd(base::OpenFile(output_name, O_CREAT | O_RDWR, 0600));
-    if (!fd) {
-      PERFETTO_PLOG("Failed to create file: %s", output_name.c_str());
-      return 1;
-    }
+    if (!fd)
+      return util::ErrStatus("Failed to create file: %s", output_name.c_str());
     int res = ftruncate(fd.get(), 0);
     PERFETTO_CHECK(res == 0);
   }
@@ -240,11 +239,10 @@
   PERFETTO_DCHECK(!attach_has_more);
 
   util::Status status = attach_it.Status();
-  if (!status.ok()) {
-    PERFETTO_ELOG("SQLite error: %s", status.c_message());
-    return 1;
-  }
+  if (!status.ok())
+    return util::ErrStatus("SQLite error: %s", status.c_message());
 
+  // Export real and virtual tables.
   auto tables_it = g_tp->ExecuteQuery(
       "SELECT name FROM perfetto_tables UNION "
       "SELECT name FROM sqlite_master WHERE type='table'");
@@ -259,26 +257,43 @@
     PERFETTO_DCHECK(!export_has_more);
 
     status = export_it.Status();
-    if (!status.ok()) {
-      PERFETTO_ELOG("SQLite error: %s", status.c_message());
-      return 1;
-    }
+    if (!status.ok())
+      return util::ErrStatus("SQLite error: %s", status.c_message());
   }
   status = tables_it.Status();
-  if (!status.ok()) {
-    PERFETTO_ELOG("SQLite error: %s", status.c_message());
-    return 1;
+  if (!status.ok())
+    return util::ErrStatus("SQLite error: %s", status.c_message());
+
+  // Export views.
+  auto views_it =
+      g_tp->ExecuteQuery("SELECT sql FROM sqlite_master WHERE type='view'");
+  for (uint32_t rows = 0; views_it.Next(); rows++) {
+    std::string sql = views_it.Get(0).string_value;
+    // View statements are of the form "CREATE VIEW name AS stmt". We need to
+    // rewrite name to point to the exported db.
+    const std::string kPrefix = "CREATE VIEW ";
+    PERFETTO_CHECK(sql.find(kPrefix) == 0);
+    sql = sql.substr(0, kPrefix.size()) + "perfetto_export." +
+          sql.substr(kPrefix.size());
+
+    auto export_it = g_tp->ExecuteQuery(sql);
+    bool export_has_more = export_it.Next();
+    PERFETTO_DCHECK(!export_has_more);
+
+    status = export_it.Status();
+    if (!status.ok())
+      return util::ErrStatus("SQLite error: %s", status.c_message());
   }
+  status = views_it.Status();
+  if (!status.ok())
+    return util::ErrStatus("SQLite error: %s", status.c_message());
 
   auto detach_it = g_tp->ExecuteQuery("DETACH DATABASE perfetto_export");
   bool detach_has_more = attach_it.Next();
   PERFETTO_DCHECK(!detach_has_more);
   status = detach_it.Status();
-  if (!status.ok()) {
-    PERFETTO_ELOG("SQLite error: %s", status.c_message());
-    return 1;
-  }
-  return 0;
+  return status.ok() ? util::OkStatus()
+                     : util::ErrStatus("SQLite error: %s", status.c_message());
 }
 
 class ErrorPrinter : public google::protobuf::io::ErrorCollector {
@@ -327,6 +342,23 @@
   google::protobuf::compiler::Parser parser;
   parser.Parse(&tokenizer, file_desc);
 
+  // Go through all the imports (dependencies) and make the import
+  // paths relative to the Perfetto root. This allows trace processor embedders
+  // to have paths relative to their own root for imports when using metric
+  // proto extensions.
+  for (int i = 0; i < file_desc->dependency_size(); ++i) {
+    static constexpr char kPrefix[] = "protos/perfetto/metrics/";
+    auto* dep = file_desc->mutable_dependency(i);
+
+    // If the file being imported contains kPrefix, it is probably an import of
+    // a Perfetto metrics proto. Strip anything before kPrefix to ensure that
+    // we resolve the paths correctly.
+    size_t idx = dep->find(kPrefix);
+    if (idx != std::string::npos) {
+      *dep = dep->substr(idx);
+    }
+  }
+
   file_desc->set_name(BaseName(extend_metrics_proto));
   pool->BuildFile(*file_desc);
 
@@ -345,21 +377,21 @@
   kNone,
 };
 
-int RunMetrics(const std::vector<std::string>& metric_names,
-               OutputFormat format,
-               const google::protobuf::DescriptorPool& pool) {
+util::Status RunMetrics(const std::vector<std::string>& metric_names,
+                        OutputFormat format,
+                        const google::protobuf::DescriptorPool& pool) {
   std::vector<uint8_t> metric_result;
   util::Status status = g_tp->ComputeMetric(metric_names, &metric_result);
   if (!status.ok()) {
-    PERFETTO_ELOG("Error when computing metrics: %s", status.c_message());
-    return 1;
+    return util::ErrStatus("Error when computing metrics: %s",
+                           status.c_message());
   }
   if (format == OutputFormat::kNone) {
-    return 0;
+    return util::OkStatus();
   }
   if (format == OutputFormat::kBinaryProto) {
     fwrite(metric_result.data(), sizeof(uint8_t), metric_result.size(), stdout);
-    return 0;
+    return util::OkStatus();
   }
 
   google::protobuf::DynamicMessageFactory factory(&pool);
@@ -391,7 +423,7 @@
     case OutputFormat::kNone:
       PERFETTO_FATAL("Unsupported output format.");
   }
-  return 0;
+  return util::OkStatus();
 }
 
 void PrintQueryResultInteractively(TraceProcessor::Iterator* it,
@@ -463,7 +495,7 @@
       ".reset       Destroys all tables/view created by the user.\n");
 }
 
-int StartInteractiveShell(uint32_t column_width) {
+util::Status StartInteractiveShell(uint32_t column_width) {
   SetupLineEditor();
 
   for (;;) {
@@ -481,7 +513,7 @@
       } else if (strcmp(command, "help") == 0) {
         PrintShellUsage();
       } else if (strcmp(command, "dump") == 0 && strlen(arg)) {
-        if (ExportTraceToDatabase(arg) != 0)
+        if (!ExportTraceToDatabase(arg).ok())
           PERFETTO_ELOG("Database export failed");
       } else if (strcmp(command, "reset") == 0) {
         g_tp->RestoreInitialTables();
@@ -495,7 +527,7 @@
     auto it = g_tp->ExecuteQuery(line.get());
     PrintQueryResultInteractively(&it, t_start, column_width);
   }
-  return 0;
+  return util::OkStatus();
 }
 
 util::Status PrintQueryResultAsCsv(TraceProcessor::Iterator* it, FILE* output) {
@@ -535,32 +567,37 @@
   return it->Status();
 }
 
-bool IsBlankLine(char* buffer) {
-  size_t buf_size = strlen(buffer);
-  for (size_t i = 0; i < buf_size; ++i) {
-    // We can index into buffer[i+1], because strlen does not include the
-    // trailing \0, so even if \r is the last character, this is not out
-    // of bound.
-    if (buffer[i] == '\r') {
-      if (buffer[i + 1] != '\n')
-        return false;
-    } else if (buffer[i] != ' ' && buffer[i] != '\t' && buffer[i] != '\n') {
-      return false;
-    }
-  }
-  return true;
+bool IsBlankLine(const std::string& buffer) {
+  return buffer == "\n" || buffer == "\r\n";
 }
 
-bool LoadQueries(FILE* input, std::vector<std::string>* output) {
+bool IsCommentLine(const std::string& buffer) {
+  return base::StartsWith(buffer, "--");
+}
+
+bool HasEndOfQueryDelimiter(const std::string& buffer) {
+  return base::EndsWith(buffer, ";\n") || base::EndsWith(buffer, ";") ||
+         base::EndsWith(buffer, ";\r\n");
+}
+
+util::Status LoadQueries(FILE* input, std::vector<std::string>* output) {
   char buffer[4096];
   while (!feof(input) && !ferror(input)) {
     std::string sql_query;
     while (fgets(buffer, sizeof(buffer), input)) {
-      if (IsBlankLine(buffer))
+      std::string line = buffer;
+      if (IsBlankLine(line))
         break;
-      sql_query.append(buffer);
+
+      if (IsCommentLine(line))
+        continue;
+
+      sql_query.append(line);
+
+      if (HasEndOfQueryDelimiter(line))
+        break;
     }
-    if (sql_query.back() == '\n')
+    if (!sql_query.empty() && sql_query.back() == '\n')
       sql_query.resize(sql_query.size() - 1);
 
     // If we have a new line at the end of the file or an extra new line
@@ -572,14 +609,13 @@
     output->push_back(sql_query);
   }
   if (ferror(input)) {
-    PERFETTO_ELOG("Error reading query file");
-    return false;
+    return util::ErrStatus("Error reading query file");
   }
-  return true;
+  return util::OkStatus();
 }
 
-bool RunQueryAndPrintResult(const std::vector<std::string> queries,
-                            FILE* output) {
+util::Status RunQueryAndPrintResult(const std::vector<std::string>& queries,
+                                    FILE* output) {
   bool is_first_query = true;
   bool is_query_error = false;
   bool has_output = false;
@@ -619,31 +655,28 @@
       is_query_error = true;
     }
   }
-  return !is_query_error;
+  return is_query_error
+             ? util::ErrStatus("Encountered errors while running queries")
+             : util::OkStatus();
 }
 
-int MaybePrintPerfFile(const std::string& perf_file_path,
-                       base::TimeNanos t_load,
-                       base::TimeNanos t_run) {
-  if (perf_file_path.empty())
-    return 0;
-
+util::Status PrintPerfFile(const std::string& perf_file_path,
+                           base::TimeNanos t_load,
+                           base::TimeNanos t_run) {
   char buf[128];
   int count = snprintf(buf, sizeof(buf), "%" PRId64 ",%" PRId64,
                        static_cast<int64_t>(t_load.count()),
                        static_cast<int64_t>(t_run.count()));
   if (count < 0) {
-    PERFETTO_ELOG("Failed to write perf data");
-    return 1;
+    return util::ErrStatus("Failed to write perf data");
   }
 
   auto fd(base::OpenFile(perf_file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666));
   if (!fd) {
-    PERFETTO_ELOG("Failed to open perf file");
-    return 1;
+    return util::ErrStatus("Failed to open perf file");
   }
   base::WriteAll(fd.get(), buf, static_cast<size_t>(count));
-  return 0;
+  return util::OkStatus();
 }
 
 struct CommandLineOptions {
@@ -652,7 +685,6 @@
   std::string sqlite_file_path;
   std::string metric_names;
   std::string metric_output;
-  std::string metric_extra;
   std::string trace_file_path;
   bool launch_shell = false;
   bool enable_httpd = false;
@@ -710,10 +742,6 @@
   return command_line_options;
 }
 
-util::Status RegisterExtraMetrics(const std::string&, const std::string&) {
-  return util::ErrStatus("RegisterExtraMetrics not implemented on Windows");
-}
-
 #else  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 
 void PrintUsage(char** argv) {
@@ -740,7 +768,9 @@
  -i, --interactive                    Starts interactive mode even after a query
                                       file is specified with -q or
                                       --run-metrics.
- -e, --export FILE                    Export the trace into a SQLite database.
+ -e, --export FILE                    Export the contents of trace processor
+                                      into an SQLite database after running any
+                                      metrics or queries specified.
  --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
@@ -750,10 +780,6 @@
                                       specified in either proto binary, proto
                                       text format or JSON format (default: proto
                                       text).
- --extra-metrics PATH                 Registers all SQL files at the given path
-                                      to the trace processor and extends the
-                                      builtin metrics proto with
-                                      $PATH/metrics-ext.proto.
  --full-sort                          Forces the trace processor into performing
                                       a full sort ignoring any windowing
                                       logic.)",
@@ -765,7 +791,6 @@
   enum LongOption {
     OPT_RUN_METRICS = 1000,
     OPT_METRICS_OUTPUT,
-    OPT_EXTRA_METRICS,
     OPT_FORCE_FULL_SORT,
   };
 
@@ -781,7 +806,6 @@
       {"export", required_argument, nullptr, 'e'},
       {"run-metrics", required_argument, nullptr, OPT_RUN_METRICS},
       {"metrics-output", required_argument, nullptr, OPT_METRICS_OUTPUT},
-      {"extra-metrics", required_argument, nullptr, OPT_EXTRA_METRICS},
       {"full-sort", no_argument, nullptr, OPT_FORCE_FULL_SORT},
       {nullptr, 0, nullptr, 0}};
 
@@ -848,11 +872,6 @@
       continue;
     }
 
-    if (option == OPT_EXTRA_METRICS) {
-      command_line_options.metric_extra = optarg;
-      continue;
-    }
-
     if (option == OPT_FORCE_FULL_SORT) {
       command_line_options.force_full_sort = true;
       continue;
@@ -864,7 +883,8 @@
 
   command_line_options.launch_shell =
       explicit_interactive || (command_line_options.metric_names.empty() &&
-                               command_line_options.query_file_path.empty());
+                               command_line_options.query_file_path.empty() &&
+                               command_line_options.sqlite_file_path.empty());
 
   // Only allow non-interactive queries to emit perf data.
   if (!command_line_options.perf_file_path.empty() &&
@@ -885,43 +905,6 @@
   return command_line_options;
 }
 
-util::Status RegisterExtraMetric(const std::string& parent_path,
-                                 const std::string& path) {
-  // Silently ignore any non-SQL files.
-  if (path.find(".sql") == std::string::npos)
-    return util::OkStatus();
-
-  std::string sql;
-  base::ReadFile(parent_path + "/" + path, &sql);
-  return g_tp->RegisterMetric(path, sql);
-}
-
-util::Status RegisterExtraMetrics(const std::string& path,
-                                  const std::string& group) {
-  std::string full_path = path + "/" + group;
-  DIR* dir = opendir(full_path.c_str());
-  if (dir == nullptr) {
-    return util::ErrStatus(
-        "Failed to open directory %s to register extra metrics",
-        full_path.c_str());
-  }
-
-  for (auto* dirent = readdir(dir); dirent != nullptr; dirent = readdir(dir)) {
-    util::Status status = util::OkStatus();
-    if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0)
-      continue;
-
-    if (dirent->d_type == DT_DIR) {
-      status = RegisterExtraMetrics(path, group + dirent->d_name + "/");
-    } else if (dirent->d_type == DT_REG) {
-      status = RegisterExtraMetric(path, group + dirent->d_name);
-    }
-    if (!status.ok())
-      return status;
-  }
-  return util::OkStatus();
-}
-
 #endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 
 void ExtendPoolWithBinaryDescriptor(google::protobuf::DescriptorPool& pool,
@@ -934,80 +917,56 @@
   }
 }
 
-int TraceProcessorMain(int argc, char** argv) {
-  CommandLineOptions options = ParseCommandLineOptions(argc, argv);
+util::Status LoadTrace(const std::string& trace_file_path, double* size_mb) {
+  util::Status read_status =
+      ReadTrace(g_tp, trace_file_path.c_str(), [&size_mb](size_t parsed_size) {
+        *size_mb = parsed_size / 1E6;
+        fprintf(stderr, "\rLoading trace: %.2f MB\r", *size_mb);
+      });
+  if (!read_status.ok()) {
+    return util::ErrStatus("Could not read trace file (path: %s): %s",
+                           trace_file_path.c_str(), read_status.c_message());
+  }
 
-  // Load the trace file into the trace processor.
-  Config config;
-  config.force_full_sort = options.force_full_sort;
-
-  std::unique_ptr<TraceProcessor> tp = TraceProcessor::CreateInstance(config);
-  g_tp = tp.get();
-
-  base::TimeNanos t_load{};
-  if (!options.trace_file_path.empty()) {
-    auto t_load_start = base::GetWallTimeNs();
-    double size_mb = 0;
-    util::Status read_status =
-        ReadTrace(tp.get(), options.trace_file_path.c_str(),
-                  [&size_mb](size_t parsed_size) {
-                    size_mb = parsed_size / 1E6;
-                    fprintf(stderr, "\rLoading trace: %.2f MB\r", size_mb);
-                  });
-    if (!read_status.ok()) {
-      PERFETTO_ELOG("Could not read trace file (path: %s): %s",
-                    options.trace_file_path.c_str(), read_status.c_message());
-      return 1;
-    }
-
-    std::unique_ptr<profiling::Symbolizer> symbolizer;
-    auto binary_path = profiling::GetPerfettoBinaryPath();
-    if (!binary_path.empty()) {
+  std::unique_ptr<profiling::Symbolizer> symbolizer;
+  auto binary_path = profiling::GetPerfettoBinaryPath();
+  if (!binary_path.empty()) {
 #if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
       symbolizer.reset(new profiling::LocalSymbolizer(std::move(binary_path)));
 #else
       PERFETTO_FATAL("This build does not support local symbolization.");
 #endif
-    }
-    if (symbolizer) {
-      profiling::SymbolizeDatabase(
-          tp.get(), symbolizer.get(), [&tp](const std::string& trace_proto) {
-            std::unique_ptr<uint8_t[]> buf(new uint8_t[trace_proto.size()]);
-            memcpy(buf.get(), trace_proto.data(), trace_proto.size());
-            auto status = tp->Parse(std::move(buf), trace_proto.size());
-            if (!status.ok()) {
-              PERFETTO_DFATAL_OR_ELOG("Failed to parse: %s",
-                                      status.message().c_str());
-              return;
-            }
-          });
-      tp->NotifyEndOfFile();
-    }
-
-    t_load = base::GetWallTimeNs() - t_load_start;
-    double t_load_s = t_load.count() / 1E9;
-    PERFETTO_ILOG("Trace loaded: %.2f MB (%.1f MB/s)", size_mb,
-                  size_mb / t_load_s);
-  }  // if (!trace_file_path.empty())
-
-#if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD)
-  if (options.enable_httpd) {
-    RunHttpRPCServer(std::move(tp));
-    return 0;
-  }
-#endif
-
-#if PERFETTO_HAS_SIGNAL_H()
-  signal(SIGINT, [](int) { g_tp->InterruptQuery(); });
-#endif
-
-  // Print out the stats to stderr for the trace.
-  if (!PrintStats()) {
-    return 1;
   }
 
-  auto t_run_start = base::GetWallTimeNs();
+  if (symbolizer) {
+    profiling::SymbolizeDatabase(
+        g_tp, symbolizer.get(), [](const std::string& trace_proto) {
+          std::unique_ptr<uint8_t[]> buf(new uint8_t[trace_proto.size()]);
+          memcpy(buf.get(), trace_proto.data(), trace_proto.size());
+          auto status = g_tp->Parse(std::move(buf), trace_proto.size());
+          if (!status.ok()) {
+            PERFETTO_DFATAL_OR_ELOG("Failed to parse: %s",
+                                    status.message().c_str());
+            return;
+          }
+        });
+    g_tp->NotifyEndOfFile();
+  }
+  return util::OkStatus();
+}
 
+util::Status RunQueries(const CommandLineOptions& options) {
+  std::vector<std::string> queries;
+  base::ScopedFstream file(fopen(options.query_file_path.c_str(), "r"));
+  if (!file) {
+    return util::ErrStatus("Could not open query file (path: %s)",
+                           options.query_file_path.c_str());
+  }
+  RETURN_IF_ERROR(LoadQueries(file.get(), &queries));
+  return RunQueryAndPrintResult(queries, stdout);
+}
+
+util::Status RunMetrics(const CommandLineOptions& options) {
   // Descriptor pool used for printing output as textproto.
   // Building on top of generated pool so default protos in
   // google.protobuf.descriptor.proto are available.
@@ -1018,106 +977,104 @@
   ExtendPoolWithBinaryDescriptor(pool, kCustomOptionsDescriptor.data(),
                                  kCustomOptionsDescriptor.size());
 
-  if (!options.metric_extra.empty()) {
-    util::Status status = RegisterExtraMetrics(options.metric_extra, "");
+  std::vector<std::string> metrics;
+  for (base::StringSplitter ss(options.metric_names, ','); ss.Next();) {
+    metrics.emplace_back(ss.cur_token());
+  }
+
+  // For all metrics which are files, register them and extend the metrics
+  // proto.
+  for (size_t i = 0; i < metrics.size(); ++i) {
+    const std::string& metric_or_path = metrics[i];
+
+    // If there is no extension, we assume it is a builtin metric.
+    auto ext_idx = metric_or_path.rfind(".");
+    if (ext_idx == std::string::npos)
+      continue;
+
+    std::string no_ext_name = metric_or_path.substr(0, ext_idx);
+    util::Status status = RegisterMetric(no_ext_name + ".sql");
     if (!status.ok()) {
-      PERFETTO_ELOG("Failed to register extra metrics: %s", status.c_message());
-      return 1;
+      return util::ErrStatus("Unable to register metric %s: %s",
+                             metric_or_path.c_str(), status.c_message());
     }
 
-    auto ext_proto = options.metric_extra + "/metrics-ext.proto";
-    // Check if the file exists
-    base::ScopedFile file(base::OpenFile(ext_proto, O_RDONLY));
-    if (file.get() != -1) {
-      status = ExtendMetricsProto(ext_proto, &pool);
-      if (!status.ok()) {
-        PERFETTO_ELOG("Failed to extend metrics proto: %s", status.c_message());
-        return 1;
-      }
+    status = ExtendMetricsProto(no_ext_name + ".proto", &pool);
+    if (!status.ok()) {
+      return util::ErrStatus("Unable to extend metrics proto %s: %s",
+                             metric_or_path.c_str(), status.c_message());
     }
+
+    metrics[i] = BaseName(no_ext_name);
   }
 
-  if (!options.metric_names.empty()) {
-    std::vector<std::string> metrics;
-    for (base::StringSplitter ss(options.metric_names, ','); ss.Next();) {
-      metrics.emplace_back(ss.cur_token());
-    }
-
-    // For all metrics which are files, register them and extend the metrics
-    // proto.
-    for (size_t i = 0; i < metrics.size(); ++i) {
-      const std::string& metric_or_path = metrics[i];
-
-      // If there is no extension, we assume it is a builtin metric.
-      auto ext_idx = metric_or_path.rfind(".");
-      if (ext_idx == std::string::npos)
-        continue;
-
-      std::string no_ext_name = metric_or_path.substr(0, ext_idx);
-      util::Status status = RegisterMetric(no_ext_name + ".sql");
-      if (!status.ok()) {
-        PERFETTO_ELOG("Unable to register metric %s: %s",
-                      metric_or_path.c_str(), status.c_message());
-        return 1;
-      }
-
-      status = ExtendMetricsProto(no_ext_name + ".proto", &pool);
-      if (!status.ok()) {
-        PERFETTO_ELOG("Unable to extend metrics proto %s: %s",
-                      metric_or_path.c_str(), status.c_message());
-        return 1;
-      }
-
-      metrics[i] = BaseName(no_ext_name);
-    }
-
-    OutputFormat format;
-    if (!options.query_file_path.empty()) {
-      format = OutputFormat::kNone;
-    } else if (options.metric_output == "binary") {
-      format = OutputFormat::kBinaryProto;
-    } else if (options.metric_output == "json") {
-      format = OutputFormat::kJson;
-    } else {
-      format = OutputFormat::kTextProto;
-    }
-    int ret = RunMetrics(std::move(metrics), format, pool);
-    if (!ret) {
-      auto t_query = base::GetWallTimeNs() - t_run_start;
-      ret = MaybePrintPerfFile(options.perf_file_path, t_load, t_query);
-    }
-    if (ret)
-      return ret;
-  }
-
-  // If we were given a query file, load contents
-  std::vector<std::string> queries;
+  OutputFormat format;
   if (!options.query_file_path.empty()) {
-    base::ScopedFstream file(fopen(options.query_file_path.c_str(), "r"));
-    if (!file) {
-      PERFETTO_ELOG("Could not open query file (path: %s)",
-                    options.query_file_path.c_str());
-      return 1;
-    }
-    if (!LoadQueries(file.get(), &queries)) {
-      return 1;
-    }
+    format = OutputFormat::kNone;
+  } else if (options.metric_output == "binary") {
+    format = OutputFormat::kBinaryProto;
+  } else if (options.metric_output == "json") {
+    format = OutputFormat::kJson;
+  } else {
+    format = OutputFormat::kTextProto;
+  }
+  return RunMetrics(std::move(metrics), format, pool);
+}
+
+util::Status TraceProcessorMain(int argc, char** argv) {
+  CommandLineOptions options = ParseCommandLineOptions(argc, argv);
+
+  Config config;
+  config.force_full_sort = options.force_full_sort;
+
+  std::unique_ptr<TraceProcessor> tp = TraceProcessor::CreateInstance(config);
+  g_tp = tp.get();
+
+  base::TimeNanos t_load{};
+  if (!options.trace_file_path.empty()) {
+    base::TimeNanos t_load_start = base::GetWallTimeNs();
+    double size_mb = 0;
+    RETURN_IF_ERROR(LoadTrace(options.trace_file_path, &size_mb));
+    t_load = base::GetWallTimeNs() - t_load_start;
+
+    double t_load_s = t_load.count() / 1E9;
+    PERFETTO_ILOG("Trace loaded: %.2f MB (%.1f MB/s)", size_mb,
+                  size_mb / t_load_s);
+
+    RETURN_IF_ERROR(PrintStats());
   }
 
-  if (!RunQueryAndPrintResult(queries, stdout)) {
-    return 1;
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD)
+  if (options.enable_httpd) {
+    RunHttpRPCServer(std::move(tp));
+    PERFETTO_FATAL("Should never return");
   }
+#endif
+
+#if PERFETTO_HAS_SIGNAL_H()
+  signal(SIGINT, [](int) { g_tp->InterruptQuery(); });
+#endif
+
+  base::TimeNanos t_query_start = base::GetWallTimeNs();
+  if (!options.metric_names.empty()) {
+    RETURN_IF_ERROR(RunMetrics(options));
+  }
+
+  if (!options.query_file_path.empty()) {
+    RETURN_IF_ERROR(RunQueries(options));
+  }
+  base::TimeNanos t_query = base::GetWallTimeNs() - t_query_start;
 
   if (!options.sqlite_file_path.empty()) {
-    return ExportTraceToDatabase(options.sqlite_file_path);
+    RETURN_IF_ERROR(ExportTraceToDatabase(options.sqlite_file_path));
   }
 
-  if (!options.launch_shell) {
-    auto t_query = base::GetWallTimeNs() - t_run_start;
-    return MaybePrintPerfFile(options.perf_file_path, t_load, t_query);
+  if (options.launch_shell) {
+    RETURN_IF_ERROR(StartInteractiveShell(options.wide ? 40 : 20));
+  } else if (!options.perf_file_path.empty()) {
+    RETURN_IF_ERROR(PrintPerfFile(options.perf_file_path, t_load, t_query));
   }
-
-  return StartInteractiveShell(options.wide ? 40 : 20);
+  return util::OkStatus();
 }
 
 }  // namespace
@@ -1126,5 +1083,10 @@
 }  // namespace perfetto
 
 int main(int argc, char** argv) {
-  return perfetto::trace_processor::TraceProcessorMain(argc, argv);
+  auto status = perfetto::trace_processor::TraceProcessorMain(argc, argv);
+  if (!status.ok()) {
+    PERFETTO_ELOG("%s", status.c_message());
+    return 1;
+  }
+  return 0;
 }
diff --git a/src/trace_processor/trace_processor_storage_impl.cc b/src/trace_processor/trace_processor_storage_impl.cc
index a8d8dee..5276196 100644
--- a/src/trace_processor/trace_processor_storage_impl.cc
+++ b/src/trace_processor/trace_processor_storage_impl.cc
@@ -17,22 +17,22 @@
 #include "src/trace_processor/trace_processor_storage_impl.h"
 
 #include "perfetto/base/logging.h"
-#include "src/trace_processor/args_tracker.h"
-#include "src/trace_processor/clock_tracker.h"
-#include "src/trace_processor/event_tracker.h"
 #include "src/trace_processor/forwarding_trace_parser.h"
-#include "src/trace_processor/heap_profile_tracker.h"
-#include "src/trace_processor/importers/ftrace/ftrace_module.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/clock_tracker.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/importers/default_modules.h"
+#include "src/trace_processor/importers/proto/heap_profile_tracker.h"
+#include "src/trace_processor/importers/proto/metadata_tracker.h"
+#include "src/trace_processor/importers/proto/perf_sample_tracker.h"
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
 #include "src/trace_processor/importers/proto/proto_trace_tokenizer.h"
-#include "src/trace_processor/importers/proto/track_event_module.h"
-#include "src/trace_processor/metadata_tracker.h"
-#include "src/trace_processor/process_tracker.h"
-#include "src/trace_processor/slice_tracker.h"
-#include "src/trace_processor/stack_profile_tracker.h"
+#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
 #include "src/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/trace_sorter.h"
-#include "src/trace_processor/track_tracker.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -49,14 +49,9 @@
   context_.heap_profile_tracker.reset(new HeapProfileTracker(&context_));
   context_.metadata_tracker.reset(new MetadataTracker(&context_));
   context_.global_args_tracker.reset(new GlobalArgsTracker(&context_));
+  context_.perf_sample_tracker.reset(new PerfSampleTracker(&context_));
 
-  context_.modules.emplace_back(new FtraceModule());
-  // Ftrace module is special, because it has one extra method for parsing
-  // ftrace packets. So we need to store a pointer to it separately.
-  context_.ftrace_module =
-      static_cast<FtraceModule*>(context_.modules.back().get());
-
-  context_.modules.emplace_back(new TrackEventModule(&context_));
+  RegisterDefaultModules(&context_);
 }
 
 TraceProcessorStorageImpl::~TraceProcessorStorageImpl() {}
@@ -82,10 +77,15 @@
   if (unrecoverable_parse_error_ || !context_.chunk_reader)
     return;
 
+  context_.chunk_reader->NotifyEndOfFile();
   if (context_.sorter)
     context_.sorter->ExtractEventsForced();
   context_.event_tracker->FlushPendingEvents();
   context_.slice_tracker->FlushPendingSlices();
+  context_.heap_profile_tracker->NotifyEndOfFile();
+  for (std::unique_ptr<ProtoImporterModule>& module : context_.modules) {
+    module->NotifyEndOfFile();
+  }
 }
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/trace_processor_storage_impl.h b/src/trace_processor/trace_processor_storage_impl.h
index 41f1273..1c029c6 100644
--- a/src/trace_processor/trace_processor_storage_impl.h
+++ b/src/trace_processor/trace_processor_storage_impl.h
@@ -22,7 +22,7 @@
 #include "perfetto/trace_processor/basic_types.h"
 #include "perfetto/trace_processor/status.h"
 #include "perfetto/trace_processor/trace_processor_storage.h"
-#include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 
 namespace perfetto {
 namespace trace_processor {
diff --git a/src/trace_processor/trace_sorter.cc b/src/trace_processor/trace_sorter.cc
index 49cd78e..677d31a 100644
--- a/src/trace_processor/trace_sorter.cc
+++ b/src/trace_processor/trace_sorter.cc
@@ -24,8 +24,9 @@
 namespace perfetto {
 namespace trace_processor {
 
-TraceSorter::TraceSorter(TraceProcessorContext* context, int64_t window_size_ns)
-    : context_(context), window_size_ns_(window_size_ns) {
+TraceSorter::TraceSorter(std::unique_ptr<TraceParser> parser,
+                         int64_t window_size_ns)
+    : parser_(std::move(parser)), window_size_ns_(window_size_ns) {
   const char* env = getenv("TRACE_PROCESSOR_SORT_ONLY");
   bypass_next_stage_for_testing_ = env && !strcmp(env, "1");
   if (bypass_next_stage_for_testing_)
@@ -81,7 +82,6 @@
   constexpr int64_t kTsMax = std::numeric_limits<int64_t>::max();
   const bool was_empty = global_min_ts_ == kTsMax && global_max_ts_ == 0;
   int64_t extract_end_ts = global_max_ts_ - window_size_ns;
-  auto* next_stage = context_->parser.get();
   size_t iterations = 0;
   for (;; iterations++) {
     size_t min_queue_idx = 0;  // The index of the queue with the min(ts).
@@ -137,11 +137,11 @@
 
       if (min_queue_idx == 0) {
         // queues_[0] is for non-ftrace packets.
-        next_stage->ParseTracePacket(timestamp, std::move(event));
+        parser_->ParseTracePacket(timestamp, std::move(event));
       } else {
         // Ftrace queues start at offset 1. So queues_[1] = cpu[0] and so on.
         uint32_t cpu = static_cast<uint32_t>(min_queue_idx - 1);
-        next_stage->ParseFtracePacket(cpu, timestamp, std::move(event));
+        parser_->ParseFtracePacket(cpu, timestamp, std::move(event));
       }
     }  // for (event: events)
 
diff --git a/src/trace_processor/trace_sorter.h b/src/trace_processor/trace_sorter.h
index b1ccf56..a42e9b0 100644
--- a/src/trace_processor/trace_sorter.h
+++ b/src/trace_processor/trace_sorter.h
@@ -21,10 +21,9 @@
 
 #include "perfetto/ext/base/circular_queue.h"
 #include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/timestamped_trace_piece.h"
 #include "src/trace_processor/trace_blob_view.h"
-#include "src/trace_processor/trace_processor_context.h"
-#include "src/trace_processor/trace_storage.h"
 
 namespace Json {
 class Value;
@@ -35,6 +34,7 @@
 
 class FuchsiaProviderView;
 class PacketSequenceState;
+struct SystraceLine;
 
 // This class takes care of sorting events parsed from the trace stream in
 // arbitrary order and pushing them to the next pipeline stages (parsing) in
@@ -65,7 +65,7 @@
 // from there to the end.
 class TraceSorter {
  public:
-  TraceSorter(TraceProcessorContext*, int64_t window_size_ns);
+  TraceSorter(std::unique_ptr<TraceParser> parser, int64_t window_size_ns);
 
   inline void PushTracePacket(int64_t timestamp,
                               PacketSequenceState* state,
@@ -95,6 +95,15 @@
     MaybeExtractEvents(queue);
   }
 
+  inline void PushSystraceLine(std::unique_ptr<SystraceLine> systrace_line) {
+    DCHECK_ftrace_batch_cpu(kNoBatch);
+    auto* queue = GetQueue(0);
+    int64_t timestamp = systrace_line->ts;
+    queue->Append(TimestampedTracePiece(timestamp, packet_idx_++,
+                                        std::move(systrace_line)));
+    MaybeExtractEvents(queue);
+  }
+
   inline void PushFtraceEvent(uint32_t cpu,
                               int64_t timestamp,
                               TraceBlobView event) {
@@ -131,14 +140,8 @@
   }
 
   inline void PushTrackEventPacket(int64_t timestamp,
-                                   int64_t thread_time,
-                                   int64_t thread_instruction_count,
-                                   PacketSequenceState* state,
-                                   TraceBlobView packet) {
+                                   std::unique_ptr<TrackEventData> data) {
     auto* queue = GetQueue(0);
-    std::unique_ptr<TrackEventData> data(
-        new TrackEventData{std::move(packet), state->current_generation(),
-                           thread_time, thread_instruction_count});
     queue->Append(
         TimestampedTracePiece(timestamp, packet_idx_++, std::move(data)));
     MaybeExtractEvents(queue);
@@ -236,7 +239,7 @@
     SortAndExtractEventsBeyondWindow(window_size_ns_);
   }
 
-  TraceProcessorContext* const context_;
+  std::unique_ptr<TraceParser> parser_;
 
   // queues_[0] is the general (non-ftrace) queue.
   // queues_[1] is the ftrace queue for CPU(0).
diff --git a/src/trace_processor/trace_sorter_unittest.cc b/src/trace_processor/trace_sorter_unittest.cc
index 5db7c0f..a7b2fc7 100644
--- a/src/trace_processor/trace_sorter_unittest.cc
+++ b/src/trace_processor/trace_sorter_unittest.cc
@@ -21,8 +21,8 @@
 
 #include "perfetto/trace_processor/basic_types.h"
 #include "src/trace_processor/timestamped_trace_piece.h"
-#include "src/trace_processor/trace_processor_context.h"
 #include "src/trace_processor/trace_sorter.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 #include "test/gtest_and_gmock.h"
 
 namespace perfetto {
@@ -76,10 +76,13 @@
       : test_buffer_(std::unique_ptr<uint8_t[]>(new uint8_t[8]), 0, 8) {
     storage_ = new NiceMock<MockTraceStorage>();
     context_.storage.reset(storage_);
-    context_.sorter.reset(new TraceSorter(
-        &context_, std::numeric_limits<int64_t>::max() /*window_size*/));
-    parser_ = new MockTraceParser(&context_);
-    context_.parser.reset(parser_);
+
+    std::unique_ptr<MockTraceParser> parser(new MockTraceParser(&context_));
+    parser_ = parser.get();
+
+    context_.sorter.reset(
+        new TraceSorter(std::move(parser),
+                        std::numeric_limits<int64_t>::max() /*window_size*/));
   }
 
  protected:
diff --git a/src/trace_processor/types/BUILD.gn b/src/trace_processor/types/BUILD.gn
index b86ba23..cb719b5 100644
--- a/src/trace_processor/types/BUILD.gn
+++ b/src/trace_processor/types/BUILD.gn
@@ -14,14 +14,30 @@
 
 source_set("types") {
   sources = [
+    "destructible.cc",
+    "destructible.h",
     "gfp_flags.cc",
     "gfp_flags.h",
+    "task_state.cc",
+    "task_state.h",
+    "trace_processor_context.h",
     "variadic.cc",
     "variadic.h",
   ]
   deps = [
     "../../../gn:default_deps",
     "../../../include/perfetto/ext/base",
+    "../../../include/perfetto/trace_processor",
     "../containers",
   ]
 }
+
+source_set("unittests") {
+  testonly = true
+  sources = [ "task_state_unittests.cc" ]
+  deps = [
+    ":types",
+    "../../../gn:default_deps",
+    "../../../gn:gtest_and_gmock",
+  ]
+}
diff --git a/src/trace_processor/destructible.cc b/src/trace_processor/types/destructible.cc
similarity index 93%
rename from src/trace_processor/destructible.cc
rename to src/trace_processor/types/destructible.cc
index 22bcf6a..3998441 100644
--- a/src/trace_processor/destructible.cc
+++ b/src/trace_processor/types/destructible.cc
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include "src/trace_processor/destructible.h"
+#include "src/trace_processor/types/destructible.h"
 
 namespace perfetto {
 namespace trace_processor {
diff --git a/src/trace_processor/destructible.h b/src/trace_processor/types/destructible.h
similarity index 87%
rename from src/trace_processor/destructible.h
rename to src/trace_processor/types/destructible.h
index 9b9c20f..ea63202 100644
--- a/src/trace_processor/destructible.h
+++ b/src/trace_processor/types/destructible.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_DESTRUCTIBLE_H_
-#define SRC_TRACE_PROCESSOR_DESTRUCTIBLE_H_
+#ifndef SRC_TRACE_PROCESSOR_TYPES_DESTRUCTIBLE_H_
+#define SRC_TRACE_PROCESSOR_TYPES_DESTRUCTIBLE_H_
 
 namespace perfetto {
 namespace trace_processor {
@@ -33,4 +33,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_DESTRUCTIBLE_H_
+#endif  // SRC_TRACE_PROCESSOR_TYPES_DESTRUCTIBLE_H_
diff --git a/src/trace_processor/ftrace_utils.cc b/src/trace_processor/types/task_state.cc
similarity index 72%
rename from src/trace_processor/ftrace_utils.cc
rename to src/trace_processor/types/task_state.cc
index b58fd73..758ec0b 100644
--- a/src/trace_processor/ftrace_utils.cc
+++ b/src/trace_processor/types/task_state.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/ftrace_utils.h"
+#include "src/trace_processor/types/task_state.h"
 
 #include <stdint.h>
 #include <algorithm>
@@ -26,16 +26,6 @@
 namespace trace_processor {
 namespace ftrace_utils {
 
-namespace {
-struct FtraceTime {
-  FtraceTime(int64_t ns)
-      : secs(ns / 1000000000LL), micros((ns - secs * 1000000000LL) / 1000) {}
-
-  const int64_t secs;
-  const int64_t micros;
-};
-}  // namespace
-
 TaskState::TaskState(uint16_t raw_state) {
   if (raw_state > kMaxState) {
     state_ = 0;
@@ -186,56 +176,6 @@
   return output;
 }
 
-void FormatSystracePrefix(int64_t timestamp,
-                          uint32_t cpu,
-                          uint32_t pid,
-                          uint32_t tgid,
-                          base::StringView name,
-                          base::StringWriter* writer) {
-  FtraceTime ftrace_time(timestamp);
-  if (pid == 0) {
-    name = "<idle>";
-  } else if (name == "") {
-    name = "<unknown>";
-  } else if (name == "CrRendererMain") {
-    // TODO(taylori): Remove this when crbug.com/978093 is fixed or
-    // when a better solution is found.
-    name = "CrRendererMainThread";
-  }
-
-  int64_t padding = 16 - static_cast<int64_t>(name.size());
-  if (padding > 0) {
-    writer->AppendChar(' ', static_cast<size_t>(padding));
-  }
-  for (size_t i = 0; i < name.size(); ++i) {
-    char c = name.data()[i];
-    writer->AppendChar(c == '-' ? '_' : c);
-  }
-  writer->AppendChar('-');
-
-  size_t pre_pid_pos = writer->pos();
-  writer->AppendInt(pid);
-  size_t pid_chars = writer->pos() - pre_pid_pos;
-  if (PERFETTO_LIKELY(pid_chars < 5)) {
-    writer->AppendChar(' ', 5 - pid_chars);
-  }
-
-  writer->AppendLiteral(" (");
-  if (tgid == 0) {
-    writer->AppendLiteral("-----");
-  } else {
-    writer->AppendPaddedInt<' ', 5>(tgid);
-  }
-  writer->AppendLiteral(") [");
-  writer->AppendPaddedInt<'0', 3>(cpu);
-  writer->AppendLiteral("] .... ");
-
-  writer->AppendInt(ftrace_time.secs);
-  writer->AppendChar('.');
-  writer->AppendPaddedInt<'0', 6>(ftrace_time.micros);
-  writer->AppendChar(':');
-}
-
 }  // namespace ftrace_utils
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/ftrace_utils.h b/src/trace_processor/types/task_state.h
similarity index 87%
rename from src/trace_processor/ftrace_utils.h
rename to src/trace_processor/types/task_state.h
index fa8d48f..0b79277 100644
--- a/src/trace_processor/ftrace_utils.h
+++ b/src/trace_processor/types/task_state.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_FTRACE_UTILS_H_
-#define SRC_TRACE_PROCESSOR_FTRACE_UTILS_H_
+#ifndef SRC_TRACE_PROCESSOR_TYPES_TASK_STATE_H_
+#define SRC_TRACE_PROCESSOR_TYPES_TASK_STATE_H_
 
 #include <stddef.h>
 #include <array>
@@ -94,15 +94,8 @@
   uint16_t state_ = 0;
 };
 
-void FormatSystracePrefix(int64_t timestamp,
-                          uint32_t cpu,
-                          uint32_t pid,
-                          uint32_t tgid,
-                          base::StringView name,
-                          base::StringWriter* writer);
-
 }  // namespace ftrace_utils
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_FTRACE_UTILS_H_
+#endif  // SRC_TRACE_PROCESSOR_TYPES_TASK_STATE_H_
diff --git a/src/trace_processor/ftrace_utils_unittest.cc b/src/trace_processor/types/task_state_unittests.cc
similarity index 96%
rename from src/trace_processor/ftrace_utils_unittest.cc
rename to src/trace_processor/types/task_state_unittests.cc
index 3675fb2..a6037ac 100644
--- a/src/trace_processor/ftrace_utils_unittest.cc
+++ b/src/trace_processor/types/task_state_unittests.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/ftrace_utils.h"
+#include "src/trace_processor/types/task_state.h"
 
 #include "test/gtest_and_gmock.h"
 
diff --git a/src/trace_processor/types/trace_processor_context.h b/src/trace_processor/types/trace_processor_context.h
new file mode 100644
index 0000000..122b686
--- /dev/null
+++ b/src/trace_processor/types/trace_processor_context.h
@@ -0,0 +1,113 @@
+/*
+ * 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_TYPES_TRACE_PROCESSOR_CONTEXT_H_
+#define SRC_TRACE_PROCESSOR_TYPES_TRACE_PROCESSOR_CONTEXT_H_
+
+#include <memory>
+#include <vector>
+
+#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/types/destructible.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class ArgsTracker;
+class AndroidProbesTracker;
+class ChunkedTraceReader;
+class ClockTracker;
+class EventTracker;
+class ForwardingTraceParser;
+class FtraceModule;
+class GlobalArgsTracker;
+class HeapGraphTracker;
+class HeapProfileTracker;
+class MetadataTracker;
+class PerfSampleTracker;
+class ProtoImporterModule;
+class ProcessTracker;
+class SliceTracker;
+class TraceParser;
+class TraceSorter;
+class TraceStorage;
+class TrackTracker;
+class JsonTracker;
+
+class TraceProcessorContext {
+ public:
+  TraceProcessorContext();
+  ~TraceProcessorContext();
+
+  Config config;
+
+  std::unique_ptr<TraceStorage> storage;
+
+  std::unique_ptr<ChunkedTraceReader> chunk_reader;
+  std::unique_ptr<TraceSorter> sorter;
+
+  // Keep the global tracker before the args tracker as we access the global
+  // tracker in the destructor of the args tracker. Also keep it before other
+  // trackers, as they may own ArgsTrackers themselves.
+  std::unique_ptr<GlobalArgsTracker> global_args_tracker;
+  std::unique_ptr<ArgsTracker> args_tracker;
+
+  std::unique_ptr<TrackTracker> track_tracker;
+  std::unique_ptr<SliceTracker> slice_tracker;
+  std::unique_ptr<ProcessTracker> process_tracker;
+  std::unique_ptr<EventTracker> event_tracker;
+  std::unique_ptr<ClockTracker> clock_tracker;
+  std::unique_ptr<HeapProfileTracker> heap_profile_tracker;
+  std::unique_ptr<MetadataTracker> metadata_tracker;
+  std::unique_ptr<PerfSampleTracker> perf_sample_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 storage_full target. To access these fields use
+  // the GetOrCreate() method on their subclass type, e.g.
+  // SyscallTracker::GetOrCreate(context)
+  std::unique_ptr<Destructible> android_probes_tracker;  // AndroidProbesTracker
+  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
+  std::unique_ptr<Destructible> json_tracker;            // JsonTracker
+
+  // These fields are trace readers which will be called by |forwarding_parser|
+  // once the format of the trace is discovered. They are placed here as they
+  // are only available in the storage_full target.
+  std::unique_ptr<ChunkedTraceReader> json_trace_tokenizer;
+  std::unique_ptr<ChunkedTraceReader> fuchsia_trace_tokenizer;
+  std::unique_ptr<ChunkedTraceReader> systrace_trace_parser;
+  std::unique_ptr<ChunkedTraceReader> gzip_trace_parser;
+
+  // These fields are trace parsers which will be called by |forwarding_parser|
+  // once the format of the trace is discovered. They are placed here as they
+  // are only available in the storage_full target.
+  std::unique_ptr<TraceParser> json_trace_parser;
+  std::unique_ptr<TraceParser> fuchsia_trace_parser;
+
+  // The module at the index N is registered to handle field id N in
+  // TracePacket.
+  std::vector<ProtoImporterModule*> modules_by_field;
+  std::vector<std::unique_ptr<ProtoImporterModule>> modules;
+  FtraceModule* ftrace_module = nullptr;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_TYPES_TRACE_PROCESSOR_CONTEXT_H_
diff --git a/src/trace_processor/util/BUILD.gn b/src/trace_processor/util/BUILD.gn
new file mode 100644
index 0000000..28f1781
--- /dev/null
+++ b/src/trace_processor/util/BUILD.gn
@@ -0,0 +1,67 @@
+# 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")
+
+source_set("util") {
+  sources = [ "status_macros.h" ]
+  deps = [
+    "../../../gn:default_deps",
+    "../../../include/perfetto/trace_processor:basic_types",
+  ]
+}
+
+source_set("protozero_to_text") {
+  sources = [
+    "protozero_to_text.cc",
+    "protozero_to_text.h",
+  ]
+  deps = [
+    ":descriptors",
+    "..:track_event_descriptor",
+    "../../../gn:default_deps",
+    "../../../protos/perfetto/common:zero",
+    "../../../protos/perfetto/trace/track_event:zero",
+    "../../base",
+    "../../protozero",
+  ]
+}
+
+source_set("descriptors") {
+  sources = [
+    "descriptors.cc",
+    "descriptors.h",
+  ]
+  deps = [
+    "..:track_event_descriptor",
+    "../../../gn:default_deps",
+    "../../../include/perfetto/trace_processor",
+    "../../../protos/perfetto/common:zero",
+    "../../base",
+    "../../protozero",
+  ]
+}
+
+source_set("unittests") {
+  sources = [ "protozero_to_text_unittests.cc" ]
+  testonly = true
+  deps = [
+    ":protozero_to_text",
+    "..:track_event_descriptor",
+    "../../../gn:default_deps",
+    "../../../gn:gtest_and_gmock",
+    "../../../protos/perfetto/trace/track_event:zero",
+    "../../protozero",
+  ]
+}
diff --git a/src/trace_processor/descriptors.cc b/src/trace_processor/util/descriptors.cc
similarity index 99%
rename from src/trace_processor/descriptors.cc
rename to src/trace_processor/util/descriptors.cc
index 7e04d32..c92488e 100644
--- a/src/trace_processor/descriptors.cc
+++ b/src/trace_processor/util/descriptors.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/descriptors.h"
+#include "src/trace_processor/util/descriptors.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/protozero/field.h"
 
diff --git a/src/trace_processor/descriptors.h b/src/trace_processor/util/descriptors.h
similarity index 97%
rename from src/trace_processor/descriptors.h
rename to src/trace_processor/util/descriptors.h
index 7dd6ded..59ea48d 100644
--- a/src/trace_processor/descriptors.h
+++ b/src/trace_processor/util/descriptors.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_DESCRIPTORS_H_
-#define SRC_TRACE_PROCESSOR_DESCRIPTORS_H_
+#ifndef SRC_TRACE_PROCESSOR_UTIL_DESCRIPTORS_H_
+#define SRC_TRACE_PROCESSOR_UTIL_DESCRIPTORS_H_
 
 #include <algorithm>
 #include <string>
@@ -162,4 +162,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_DESCRIPTORS_H_
+#endif  // SRC_TRACE_PROCESSOR_UTIL_DESCRIPTORS_H_
diff --git a/src/trace_processor/proto_to_json.cc b/src/trace_processor/util/proto_to_json.cc
similarity index 99%
rename from src/trace_processor/proto_to_json.cc
rename to src/trace_processor/util/proto_to_json.cc
index e6f22e9..6befd5e 100644
--- a/src/trace_processor/proto_to_json.cc
+++ b/src/trace_processor/util/proto_to_json.cc
@@ -22,7 +22,7 @@
 
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/string_utils.h"
-#include "src/trace_processor/proto_to_json.h"
+#include "src/trace_processor/util/proto_to_json.h"
 
 namespace perfetto {
 namespace trace_processor {
diff --git a/src/trace_processor/proto_to_json.h b/src/trace_processor/util/proto_to_json.h
similarity index 87%
rename from src/trace_processor/proto_to_json.h
rename to src/trace_processor/util/proto_to_json.h
index 90cae5c..cfe7a11 100644
--- a/src/trace_processor/proto_to_json.h
+++ b/src/trace_processor/util/proto_to_json.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_PROTO_TO_JSON_H_
-#define SRC_TRACE_PROCESSOR_PROTO_TO_JSON_H_
+#ifndef SRC_TRACE_PROCESSOR_UTIL_PROTO_TO_JSON_H_
+#define SRC_TRACE_PROCESSOR_UTIL_PROTO_TO_JSON_H_
 
 #include <google/protobuf/message.h>
 
@@ -35,4 +35,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_PROTO_TO_JSON_H_
+#endif  // SRC_TRACE_PROCESSOR_UTIL_PROTO_TO_JSON_H_
diff --git a/src/trace_processor/protozero_to_text.cc b/src/trace_processor/util/protozero_to_text.cc
similarity index 98%
rename from src/trace_processor/protozero_to_text.cc
rename to src/trace_processor/util/protozero_to_text.cc
index c33abf8..cc0f659 100644
--- a/src/trace_processor/protozero_to_text.cc
+++ b/src/trace_processor/util/protozero_to_text.cc
@@ -1,10 +1,10 @@
-#include "src/trace_processor/protozero_to_text.h"
+#include "src/trace_processor/util/protozero_to_text.h"
 
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/protozero/proto_decoder.h"
 #include "perfetto/protozero/proto_utils.h"
 #include "protos/perfetto/common/descriptor.pbzero.h"
-#include "src/trace_processor/descriptors.h"
+#include "src/trace_processor/util/descriptors.h"
 
 // This is the highest level that this protozero to text supports.
 #include "src/trace_processor/importers/proto/track_event.descriptor.h"
diff --git a/src/trace_processor/protozero_to_text.h b/src/trace_processor/util/protozero_to_text.h
similarity index 91%
rename from src/trace_processor/protozero_to_text.h
rename to src/trace_processor/util/protozero_to_text.h
index 054434a..e418926 100644
--- a/src/trace_processor/protozero_to_text.h
+++ b/src/trace_processor/util/protozero_to_text.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_PROTOZERO_TO_TEXT_H_
-#define SRC_TRACE_PROCESSOR_PROTOZERO_TO_TEXT_H_
+#ifndef SRC_TRACE_PROCESSOR_UTIL_PROTOZERO_TO_TEXT_H_
+#define SRC_TRACE_PROCESSOR_UTIL_PROTOZERO_TO_TEXT_H_
 
 #include <string>
 
@@ -46,4 +46,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_PROTOZERO_TO_TEXT_H_
+#endif  // SRC_TRACE_PROCESSOR_UTIL_PROTOZERO_TO_TEXT_H_
diff --git a/src/trace_processor/protozero_to_text_unittests.cc b/src/trace_processor/util/protozero_to_text_unittests.cc
similarity index 98%
rename from src/trace_processor/protozero_to_text_unittests.cc
rename to src/trace_processor/util/protozero_to_text_unittests.cc
index fe795ed..e172dd6 100644
--- a/src/trace_processor/protozero_to_text_unittests.cc
+++ b/src/trace_processor/util/protozero_to_text_unittests.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/protozero_to_text.h"
+#include "src/trace_processor/util/protozero_to_text.h"
 
 #include "perfetto/protozero/scattered_heap_buffer.h"
 #include "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.pbzero.h"
diff --git a/src/trace_processor/util/status_macros.h b/src/trace_processor/util/status_macros.h
new file mode 100644
index 0000000..50e2bb4
--- /dev/null
+++ b/src/trace_processor/util/status_macros.h
@@ -0,0 +1,31 @@
+/*
+ * 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_UTIL_STATUS_MACROS_H_
+#define SRC_TRACE_PROCESSOR_UTIL_STATUS_MACROS_H_
+
+#include "perfetto/trace_processor/status.h"
+
+// Evaluates |expr|, which should return a util::Status. If the status is an
+// error status, returns the status from the current function.
+#define RETURN_IF_ERROR(expr)                           \
+  do {                                                  \
+    util::Status status_macro_internal_status = (expr); \
+    if (!status_macro_internal_status.ok())             \
+      return status_macro_internal_status;              \
+  } while (0)
+
+#endif  // SRC_TRACE_PROCESSOR_UTIL_STATUS_MACROS_H_
diff --git a/src/traced/probes/BUILD.gn b/src/traced/probes/BUILD.gn
index 6acb64a..7eec236 100644
--- a/src/traced/probes/BUILD.gn
+++ b/src/traced/probes/BUILD.gn
@@ -22,51 +22,47 @@
     "../../../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",
+    "common",
     "filesystem",
+    "initial_display_state",
     "metatrace",
     "packages_list",
     "power",
     "ps",
     "sys_stats",
+    "system_info",
   ]
   sources = [
     "probes_producer.cc",
@@ -79,7 +75,7 @@
 source_set("data_source") {
   deps = [
     "../../../gn:default_deps",
-    "../../tracing",
+    "../../tracing/core",
   ]
   sources = [
     "probes_data_source.cc",
@@ -93,11 +89,14 @@
     ":probes_src",
     "../../../gn:default_deps",
     "../../../gn:gtest_and_gmock",
-    "../../tracing:test_support",
+    "../../tracing/test:test_support",
     "android_log:unittests",
+    "common:unittests",
     "filesystem:unittests",
+    "initial_display_state:unittests",
     "packages_list:unittests",
     "ps:unittests",
     "sys_stats:unittests",
+    "system_info: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/android_log/android_log_data_source.cc b/src/traced/probes/android_log/android_log_data_source.cc
index c49bec6..f63ca51 100644
--- a/src/traced/probes/android_log/android_log_data_source.cc
+++ b/src/traced/probes/android_log/android_log_data_source.cc
@@ -85,11 +85,17 @@
 
 }  // namespace
 
+// static
+const ProbesDataSource::Descriptor AndroidLogDataSource::descriptor = {
+    /*name*/ "android.log",
+    /*flags*/ Descriptor::kFlagsNone,
+};
+
 AndroidLogDataSource::AndroidLogDataSource(DataSourceConfig ds_config,
                                            base::TaskRunner* task_runner,
                                            TracingSessionID session_id,
                                            std::unique_ptr<TraceWriter> writer)
-    : ProbesDataSource(session_id, kTypeId),
+    : ProbesDataSource(session_id, &descriptor),
       task_runner_(task_runner),
       writer_(std::move(writer)),
       weak_factory_(this) {
diff --git a/src/traced/probes/android_log/android_log_data_source.h b/src/traced/probes/android_log/android_log_data_source.h
index 12f1ae8..efd9e5f 100644
--- a/src/traced/probes/android_log/android_log_data_source.h
+++ b/src/traced/probes/android_log/android_log_data_source.h
@@ -45,6 +45,8 @@
 
 class AndroidLogDataSource : public ProbesDataSource {
  public:
+  static const ProbesDataSource::Descriptor descriptor;
+
   struct Stats {
     uint64_t num_total = 0;    // Total number of log entries received.
     uint64_t num_failed = 0;   // Parser failures.
@@ -56,7 +58,6 @@
     std::string name;
     std::vector<std::string> fields;
   };
-  static constexpr int kTypeId = 6;
 
   AndroidLogDataSource(DataSourceConfig,
                        base::TaskRunner*,
diff --git a/src/traced/probes/common/BUILD.gn b/src/traced/probes/common/BUILD.gn
new file mode 100644
index 0000000..4346c4e
--- /dev/null
+++ b/src/traced/probes/common/BUILD.gn
@@ -0,0 +1,51 @@
+# 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/test.gni")
+
+source_set("common") {
+  deps = [
+    "../../../../gn:default_deps",
+    "../../../base",
+  ]
+  sources = [
+    "cpu_freq_info.cc",
+    "cpu_freq_info.h",
+  ]
+}
+
+perfetto_unittest_source_set("unittests") {
+  testonly = true
+  deps = [
+    ":common",
+    ":test_support",
+    "../../../../gn:default_deps",
+    "../../../../gn:gtest_and_gmock",
+    "../../../../src/tracing/test:test_support",
+  ]
+  sources = [ "cpu_freq_info_unittest.cc" ]
+}
+
+perfetto_unittest_source_set("test_support") {
+  testonly = true
+  deps = [
+    ":common",
+    "../../../../gn:default_deps",
+    "../../../base",
+  ]
+  sources = [
+    "cpu_freq_info_for_testing.cc",
+    "cpu_freq_info_for_testing.h",
+  ]
+}
diff --git a/src/traced/probes/common/cpu_freq_info.cc b/src/traced/probes/common/cpu_freq_info.cc
new file mode 100644
index 0000000..88f8d23
--- /dev/null
+++ b/src/traced/probes/common/cpu_freq_info.cc
@@ -0,0 +1,107 @@
+/*
+ * 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/traced/probes/common/cpu_freq_info.h"
+
+#include <set>
+
+#include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/ext/base/string_utils.h"
+
+namespace perfetto {
+
+CpuFreqInfo::CpuFreqInfo(std::string cpu_dir_path) {
+  base::ScopedDir cpu_dir(opendir(cpu_dir_path.c_str()));
+  if (!cpu_dir) {
+    PERFETTO_PLOG("Failed to opendir(%s)", cpu_dir_path.c_str());
+    return;
+  }
+  // Accumulate cpu and freqs into a set to ensure stable order.
+  std::set<std::pair</* cpu */ uint32_t, /* freq */ uint32_t>> freqs;
+  // Number of CPUs.
+  uint32_t cpus = 0;
+  while (struct dirent* dir_ent = readdir(*cpu_dir)) {
+    if (dir_ent->d_type != DT_DIR)
+      continue;
+    std::string dir_name(dir_ent->d_name);
+    if (!base::StartsWith(dir_name, "cpu"))
+      continue;
+    auto maybe_cpu_index =
+        base::StringToUInt32(base::StripPrefix(dir_name, "cpu"));
+    // There are some directories (cpufreq, cpuidle) which should be skipped.
+    if (!maybe_cpu_index.has_value())
+      continue;
+    cpus++;
+    uint32_t cpu_index = maybe_cpu_index.value();
+    std::string sys_cpu_freqs =
+        ReadFile(cpu_dir_path + "/cpu" + std::to_string(cpu_index) +
+                 "/cpufreq/scaling_available_frequencies");
+    base::StringSplitter entries(sys_cpu_freqs, ' ');
+    while (entries.Next()) {
+      auto freq = base::StringToUInt32(entries.cur_token());
+      if (freq.has_value())
+        freqs.insert({cpu_index, freq.value()});
+    }
+  }
+
+  // Build index with guards.
+  uint32_t last_cpu = 0;
+  uint32_t index = 0;
+  frequencies_index_.push_back(0);
+  for (const auto& cpu_freq : freqs) {
+    frequencies_.push_back(cpu_freq.second);
+    if (cpu_freq.first != last_cpu)
+      frequencies_index_.push_back(index);
+    last_cpu = cpu_freq.first;
+    index++;
+  }
+  frequencies_.push_back(0);
+  frequencies_index_.push_back(index);
+}
+
+CpuFreqInfo::~CpuFreqInfo() = default;
+
+CpuFreqInfo::Range CpuFreqInfo::GetFreqs(uint32_t cpu) {
+  if (cpu >= frequencies_index_.size() - 1) {
+    PERFETTO_DLOG("No frequencies for cpu%" PRIu32, cpu);
+    const uint32_t* end = frequencies_.data() + frequencies_.size();
+    return {end, end};
+  }
+  auto* start = &frequencies_[frequencies_index_[cpu]];
+  auto* end = &frequencies_[frequencies_index_[cpu + 1]];
+  return {start, end};
+}
+
+uint32_t CpuFreqInfo::GetCpuFreqIndex(uint32_t cpu, uint32_t freq) {
+  auto range = GetFreqs(cpu);
+  uint32_t index = 0;
+  for (const uint32_t* it = range.first; it != range.second; it++, index++) {
+    if (*it == freq) {
+      return static_cast<uint32_t>(frequencies_index_[cpu]) + index + 1;
+    }
+  }
+  return 0;
+}
+
+std::string CpuFreqInfo::ReadFile(std::string path) {
+  std::string contents;
+  if (!base::ReadFile(path, &contents))
+    return "";
+  return contents;
+}
+
+}  // namespace perfetto
diff --git a/src/traced/probes/common/cpu_freq_info.h b/src/traced/probes/common/cpu_freq_info.h
new file mode 100644
index 0000000..1bcdd79
--- /dev/null
+++ b/src/traced/probes/common/cpu_freq_info.h
@@ -0,0 +1,51 @@
+/*
+ * 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_TRACED_PROBES_COMMON_CPU_FREQ_INFO_H_
+#define SRC_TRACED_PROBES_COMMON_CPU_FREQ_INFO_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "perfetto/ext/base/scoped_file.h"
+
+namespace perfetto {
+
+class CpuFreqInfo {
+ public:
+  explicit CpuFreqInfo(std::string cpu_dir_path = "/sys/devices/system/cpu");
+  virtual ~CpuFreqInfo();
+
+  using Range =
+      std::pair</* begin */ const uint32_t*, /* end */ const uint32_t*>;
+  Range GetFreqs(uint32_t cpu);
+  uint32_t GetCpuFreqIndex(uint32_t cpu, uint32_t freq);
+
+ private:
+  // All frequencies of all CPUs, ordered by CPU and frequency. Includes a guard
+  // at the end.
+  std::vector<uint32_t> frequencies_;
+  // frequencies_index_[cpu] points to first frequency in frequencies_. Includes
+  // a guard at the end.
+  std::vector<size_t> frequencies_index_;
+
+  std::string ReadFile(std::string path);
+};
+
+}  // namespace perfetto
+
+#endif  // SRC_TRACED_PROBES_COMMON_CPU_FREQ_INFO_H_
diff --git a/src/traced/probes/common/cpu_freq_info_for_testing.cc b/src/traced/probes/common/cpu_freq_info_for_testing.cc
new file mode 100644
index 0000000..c974483
--- /dev/null
+++ b/src/traced/probes/common/cpu_freq_info_for_testing.cc
@@ -0,0 +1,95 @@
+/*
+ * 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/traced/probes/common/cpu_freq_info_for_testing.h"
+
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <algorithm>
+#include <memory>
+
+#include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/base/temp_file.h"
+
+namespace perfetto {
+
+namespace {
+
+const char kCpuFrequenciesAndroidLittleCore[] =
+    "300000 576000 748800 998400 1209600 1324800 1516800 1612800 1708800 \n";
+
+const char kCpuFrequenciesAndroidBigCore[] =
+    "300000 652800 825600 979200 1132800 1363200 1536000 1747200 1843200 "
+    "1996800 \n";
+
+}  // namespace
+
+CpuFreqInfoForTesting::CpuFreqInfoForTesting()
+    : fake_cpu_dir_(base::TempDir::Create()) {
+  // Create a subset of /sys/devices/system/cpu.
+  AddDir("cpuidle");
+  AddDir("cpu0");
+  AddDir("cpu0/cpufreq");
+  AddFile("cpu0/cpufreq/scaling_available_frequencies",
+          kCpuFrequenciesAndroidLittleCore);
+  AddDir("cpufreq");
+  AddDir("cpu1");
+  AddDir("cpu1/cpufreq");
+  AddFile("cpu1/cpufreq/scaling_available_frequencies",
+          kCpuFrequenciesAndroidBigCore);
+  AddDir("power");
+}
+
+CpuFreqInfoForTesting::~CpuFreqInfoForTesting() {
+  for (auto path : files_to_remove_)
+    RmFile(path);
+  std::reverse(dirs_to_remove_.begin(), dirs_to_remove_.end());
+  for (auto path : dirs_to_remove_)
+    RmDir(path);
+}
+
+std::unique_ptr<CpuFreqInfo> CpuFreqInfoForTesting::GetInstance() {
+  return std::unique_ptr<CpuFreqInfo>(new CpuFreqInfo(fake_cpu_dir_.path()));
+}
+
+void CpuFreqInfoForTesting::AddDir(std::string path) {
+  dirs_to_remove_.push_back(path);
+  mkdir(AbsolutePath(path).c_str(), 0755);
+}
+
+void CpuFreqInfoForTesting::AddFile(std::string path, std::string content) {
+  files_to_remove_.push_back(path);
+  base::ScopedFile fd(
+      base::OpenFile(AbsolutePath(path), O_WRONLY | O_CREAT | O_TRUNC, 0600));
+  PERFETTO_CHECK(base::WriteAll(fd.get(), content.c_str(), content.size()) ==
+                 static_cast<ssize_t>(content.size()));
+}
+
+void CpuFreqInfoForTesting::RmDir(std::string path) {
+  PERFETTO_CHECK(rmdir(AbsolutePath(path).c_str()) == 0);
+}
+
+void CpuFreqInfoForTesting::RmFile(std::string path) {
+  PERFETTO_CHECK(remove(AbsolutePath(path).c_str()) == 0);
+}
+
+std::string CpuFreqInfoForTesting::AbsolutePath(std::string path) {
+  return fake_cpu_dir_.path() + "/" + path;
+}
+
+}  // namespace perfetto
diff --git a/src/traced/probes/common/cpu_freq_info_for_testing.h b/src/traced/probes/common/cpu_freq_info_for_testing.h
new file mode 100644
index 0000000..e0e57c2
--- /dev/null
+++ b/src/traced/probes/common/cpu_freq_info_for_testing.h
@@ -0,0 +1,52 @@
+/*
+ * 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_TRACED_PROBES_COMMON_CPU_FREQ_INFO_FOR_TESTING_H_
+#define SRC_TRACED_PROBES_COMMON_CPU_FREQ_INFO_FOR_TESTING_H_
+
+#include "src/traced/probes/common/cpu_freq_info.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/base/temp_file.h"
+
+namespace perfetto {
+
+class CpuFreqInfoForTesting {
+ public:
+  CpuFreqInfoForTesting();
+  ~CpuFreqInfoForTesting();
+
+  std::unique_ptr<CpuFreqInfo> GetInstance();
+
+ private:
+  base::TempDir fake_cpu_dir_;
+  std::vector<std::string> dirs_to_remove_;
+  std::vector<std::string> files_to_remove_;
+
+  void AddDir(std::string path);
+  void AddFile(std::string path, std::string content);
+  void RmDir(std::string path);
+  void RmFile(std::string path);
+  std::string AbsolutePath(std::string path);
+};
+
+}  // namespace perfetto
+
+#endif  // SRC_TRACED_PROBES_COMMON_CPU_FREQ_INFO_FOR_TESTING_H_
diff --git a/src/traced/probes/common/cpu_freq_info_unittest.cc b/src/traced/probes/common/cpu_freq_info_unittest.cc
new file mode 100644
index 0000000..2886dcb
--- /dev/null
+++ b/src/traced/probes/common/cpu_freq_info_unittest.cc
@@ -0,0 +1,63 @@
+/*
+ * 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/traced/probes/common/cpu_freq_info_for_testing.h"
+
+#include "test/gtest_and_gmock.h"
+
+using ::testing::ElementsAre;
+
+namespace perfetto {
+namespace {
+
+class CpuFreqInfoTest : public ::testing::Test {
+ protected:
+  std::unique_ptr<CpuFreqInfo> GetCpuFreqInfo() {
+    return std::unique_ptr<CpuFreqInfo>(
+        cpu_freq_info_for_testing.GetInstance());
+  }
+
+  CpuFreqInfoForTesting cpu_freq_info_for_testing;
+};
+
+std::vector<uint32_t> FreqsToVector(CpuFreqInfo::Range range) {
+  std::vector<uint32_t> freqs;
+  for (auto it = range.first; it != range.second; it++)
+    freqs.push_back(*it);
+  return freqs;
+}
+
+TEST_F(CpuFreqInfoTest, CpuFreqInfo) {
+  auto cpu_freq_info = GetCpuFreqInfo();
+
+  EXPECT_THAT(FreqsToVector(cpu_freq_info->GetFreqs(0u)),
+              ElementsAre(300000, 576000, 748800, 998400, 1209600, 1324800,
+                          1516800, 1612800, 1708800));
+  EXPECT_THAT(FreqsToVector(cpu_freq_info->GetFreqs(1u)),
+              ElementsAre(300000, 652800, 825600, 979200, 1132800, 1363200,
+                          1536000, 1747200, 1843200, 1996800));
+  EXPECT_THAT(FreqsToVector(cpu_freq_info->GetFreqs(2u)), ElementsAre());
+  EXPECT_THAT(FreqsToVector(cpu_freq_info->GetFreqs(100u)), ElementsAre());
+
+  EXPECT_EQ(cpu_freq_info->GetCpuFreqIndex(0u, 300000u), 1u);
+  EXPECT_EQ(cpu_freq_info->GetCpuFreqIndex(0u, 748800u), 3u);
+  EXPECT_EQ(cpu_freq_info->GetCpuFreqIndex(1u, 300000u), 10u);
+  EXPECT_EQ(cpu_freq_info->GetCpuFreqIndex(1u, 1996800u), 19u);
+  EXPECT_EQ(cpu_freq_info->GetCpuFreqIndex(1u, 5u), 0u);
+}
+
+}  // namespace
+}  // namespace perfetto
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.cc b/src/traced/probes/filesystem/inode_file_data_source.cc
index 1f0a22f..bbe38e0 100644
--- a/src/traced/probes/filesystem/inode_file_data_source.cc
+++ b/src/traced/probes/filesystem/inode_file_data_source.cc
@@ -80,7 +80,10 @@
 }  // namespace
 
 // static
-constexpr int InodeFileDataSource::kTypeId;
+const ProbesDataSource::Descriptor InodeFileDataSource::descriptor = {
+    /*name*/ "linux.inode_file_map",
+    /*flags*/ Descriptor::kFlagsNone,
+};
 
 void CreateStaticDeviceToInodeMap(
     const std::string& root_directory,
@@ -110,7 +113,7 @@
         static_file_map,
     LRUInodeCache* cache,
     std::unique_ptr<TraceWriter> writer)
-    : ProbesDataSource(session_id, kTypeId),
+    : ProbesDataSource(session_id, &descriptor),
       task_runner_(task_runner),
       static_file_map_(static_file_map),
       cache_(cache),
@@ -297,7 +300,7 @@
 bool InodeFileDataSource::OnInodeFound(BlockDeviceID block_device_id,
                                        Inode inode_number,
                                        const std::string& path,
-                                       InodeFileMap_Entry_Type type) {
+                                       InodeFileMap_Entry_Type inode_type) {
   auto it = missing_inodes_.find(block_device_id);
   if (it == missing_inodes_.end())
     return true;
@@ -318,7 +321,7 @@
     FillInodeEntry(AddToCurrentTracePacket(block_device_id), inode_number,
                    *cur_val);
   } else {
-    InodeMapValue new_val(InodeMapValue(type, {path}));
+    InodeMapValue new_val(InodeMapValue(inode_type, {path}));
     cache_->Insert(key, new_val);
     FillInodeEntry(AddToCurrentTracePacket(block_device_id), inode_number,
                    new_val);
diff --git a/src/traced/probes/filesystem/inode_file_data_source.h b/src/traced/probes/filesystem/inode_file_data_source.h
index 8ccab1a..54a5d4d 100644
--- a/src/traced/probes/filesystem/inode_file_data_source.h
+++ b/src/traced/probes/filesystem/inode_file_data_source.h
@@ -53,7 +53,7 @@
 class InodeFileDataSource : public ProbesDataSource,
                             public FileScanner::Delegate {
  public:
-  static constexpr int kTypeId = 2;
+  static const ProbesDataSource::Descriptor descriptor;
 
   InodeFileDataSource(
       DataSourceConfig,
diff --git a/src/traced/probes/ftrace/BUILD.gn b/src/traced/probes/ftrace/BUILD.gn
index 02ef384..0ffd645 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,11 +55,12 @@
     "../../../../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",
     "cpu_stats_parser_unittest.cc",
+    "discover_vendor_tracepoints_unittest.cc",
     "event_info_unittest.cc",
     "format_parser_unittest.cc",
     "ftrace_config_muxer_unittest.cc",
@@ -78,9 +77,7 @@
     "lite",
     "zero",
   ]
-  sources = [
-    "test/test_messages.proto",
-  ]
+  sources = [ "test/test_messages.proto" ]
   proto_path = perfetto_root_path
 }
 
@@ -94,24 +91,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",
@@ -127,6 +123,8 @@
     "cpu_reader.h",
     "cpu_stats_parser.cc",
     "cpu_stats_parser.h",
+    "discover_vendor_tracepoints.cc",
+    "discover_vendor_tracepoints.h",
     "event_info.cc",
     "event_info.h",
     "event_info_constants.cc",
@@ -169,17 +167,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/atrace_hal_wrapper.cc b/src/traced/probes/ftrace/atrace_hal_wrapper.cc
index 11cdd89..78f4114 100644
--- a/src/traced/probes/ftrace/atrace_hal_wrapper.cc
+++ b/src/traced/probes/ftrace/atrace_hal_wrapper.cc
@@ -13,8 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #include "src/traced/probes/ftrace/atrace_hal_wrapper.h"
 
+#include "perfetto/base/build_config.h"
 #include "src/android_internal/atrace_hal.h"
 #include "src/android_internal/lazy_library_loader.h"
 
@@ -25,38 +27,83 @@
 }
 
 struct AtraceHalWrapper::DynamicLibLoader {
-  PERFETTO_LAZY_LOAD(android_internal::GetCategories, get_categories_);
+  PERFETTO_LAZY_LOAD(android_internal::ForgetService, forget_service_);
+  PERFETTO_LAZY_LOAD(android_internal::ListCategories, list_categories_);
+  PERFETTO_LAZY_LOAD(android_internal::EnableCategories, enable_categories_);
+  PERFETTO_LAZY_LOAD(android_internal::DisableAllCategories,
+                     disable_all_categories_);
 
-  std::vector<android_internal::TracingVendorCategory> GetCategories() {
-    if (!get_categories_)
-      return std::vector<android_internal::TracingVendorCategory>();
+  std::vector<std::string> ListCategories() {
+    std::vector<std::string> results;
+    if (!list_categories_)
+      return results;
 
     std::vector<android_internal::TracingVendorCategory> categories(
         kMaxNumCategories);
     size_t num_cat = categories.size();
-    get_categories_(&categories[0], &num_cat);
+    bool success = list_categories_(&categories[0], &num_cat);
+    if (!success)
+      return results;
     categories.resize(num_cat);
-    return categories;
+
+    for (const auto& category : categories) {
+      results.push_back(category.name);
+    }
+
+    return results;
+  }
+
+  bool EnableCategories(const std::vector<std::string>& categories) {
+    if (!enable_categories_)
+      return false;
+    std::vector<const char*> args;
+    for (const std::string& category : categories) {
+      args.push_back(category.c_str());
+    }
+    return enable_categories_(&args[0], args.size());
+  }
+
+  bool DisableAllCategories() {
+    if (!disable_all_categories_)
+      return false;
+    return disable_all_categories_();
+  }
+
+  void ForgetService() {
+    if (!forget_service_)
+      return;
+    forget_service_();
   }
 };
 
 AtraceHalWrapper::AtraceHalWrapper() {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
   lib_.reset(new DynamicLibLoader());
+#endif
 }
 
-AtraceHalWrapper::~AtraceHalWrapper() = default;
+AtraceHalWrapper::~AtraceHalWrapper() {
+  if (lib_)
+    lib_->ForgetService();
+}
 
-std::vector<AtraceHalWrapper::TracingVendorCategory>
-AtraceHalWrapper::GetAvailableCategories() {
-  auto details = lib_->GetCategories();
-  std::vector<AtraceHalWrapper::TracingVendorCategory> result;
-  for (size_t i = 0; i < details.size(); i++) {
-    AtraceHalWrapper::TracingVendorCategory cat;
-    cat.name = details[i].name;
-    cat.description = details[i].description;
-    result.emplace_back(cat);
-  }
-  return result;
+std::vector<std::string> AtraceHalWrapper::ListCategories() {
+  if (!lib_)
+    return {};
+  return lib_->ListCategories();
+}
+
+bool AtraceHalWrapper::EnableCategories(
+    const std::vector<std::string>& categories) {
+  if (!lib_)
+    return true;
+  return lib_->EnableCategories(categories);
+}
+
+bool AtraceHalWrapper::DisableAllCategories() {
+  if (!lib_)
+    return true;
+  return lib_->DisableAllCategories();
 }
 
 }  // namespace perfetto
diff --git a/src/traced/probes/ftrace/atrace_hal_wrapper.h b/src/traced/probes/ftrace/atrace_hal_wrapper.h
index db215ae..6f95fdf 100644
--- a/src/traced/probes/ftrace/atrace_hal_wrapper.h
+++ b/src/traced/probes/ftrace/atrace_hal_wrapper.h
@@ -28,17 +28,11 @@
 class AtraceHalWrapper {
  public:
   AtraceHalWrapper();
-  ~AtraceHalWrapper();
+  virtual ~AtraceHalWrapper();
 
-  struct TracingVendorCategory {
-    // The name identifying the category.
-    std::string name;
-
-    // A longer description of the category.
-    std::string description;
-  };
-
-  std::vector<TracingVendorCategory> GetAvailableCategories();
+  virtual std::vector<std::string> ListCategories();
+  virtual bool EnableCategories(const std::vector<std::string>& categories);
+  virtual bool DisableAllCategories();
 
  private:
   struct DynamicLibLoader;
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..47c5351 100644
--- a/src/traced/probes/ftrace/cpu_reader_fuzzer.cc
+++ b/src/traced/probes/ftrace/cpu_reader_fuzzer.cc
@@ -39,6 +39,7 @@
 using perfetto::protos::pbzero::FtraceEventBundle;
 
 void FuzzCpuReaderParsePage(const uint8_t* data, size_t size);
+void FuzzCpuReaderProcessPagesForDataSource(const uint8_t* data, size_t size);
 
 // TODO(rsavitski): make the fuzzer generate multi-page payloads.
 void FuzzCpuReaderProcessPagesForDataSource(const uint8_t* data, size_t size) {
@@ -52,8 +53,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/discover_vendor_tracepoints.cc b/src/traced/probes/ftrace/discover_vendor_tracepoints.cc
new file mode 100644
index 0000000..68625c0
--- /dev/null
+++ b/src/traced/probes/ftrace/discover_vendor_tracepoints.cc
@@ -0,0 +1,56 @@
+/*
+ * 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/traced/probes/ftrace/discover_vendor_tracepoints.h"
+
+#include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "src/traced/probes/ftrace/atrace_wrapper.h"
+
+namespace perfetto {
+namespace vendor_tracepoints {
+
+std::vector<GroupAndName> DiscoverTracepoints(AtraceHalWrapper* hal,
+                                              FtraceProcfs* ftrace,
+                                              const std::string& category) {
+  ftrace->DisableAllEvents();
+  hal->EnableCategories({category});
+
+  std::vector<GroupAndName> events;
+  for (const std::string& group_name : ftrace->ReadEnabledEvents()) {
+    size_t pos = group_name.find('/');
+    PERFETTO_CHECK(pos != std::string::npos);
+    events.push_back(
+        GroupAndName(group_name.substr(0, pos), group_name.substr(pos + 1)));
+  }
+
+  hal->DisableAllCategories();
+  ftrace->DisableAllEvents();
+  return events;
+}
+
+std::map<std::string, std::vector<GroupAndName>> DiscoverVendorTracepoints(
+    AtraceHalWrapper* hal,
+    FtraceProcfs* ftrace) {
+  std::map<std::string, std::vector<GroupAndName>> results;
+  for (const auto& category : hal->ListCategories()) {
+    results.emplace(category, DiscoverTracepoints(hal, ftrace, category));
+  }
+  return results;
+}
+
+}  // namespace vendor_tracepoints
+}  // namespace perfetto
diff --git a/src/traced/probes/ftrace/discover_vendor_tracepoints.h b/src/traced/probes/ftrace/discover_vendor_tracepoints.h
new file mode 100644
index 0000000..f5d791b
--- /dev/null
+++ b/src/traced/probes/ftrace/discover_vendor_tracepoints.h
@@ -0,0 +1,44 @@
+/*
+ * 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_TRACED_PROBES_FTRACE_DISCOVER_VENDOR_TRACEPOINTS_H_
+#define SRC_TRACED_PROBES_FTRACE_DISCOVER_VENDOR_TRACEPOINTS_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "src/traced/probes/ftrace/atrace_hal_wrapper.h"
+#include "src/traced/probes/ftrace/ftrace_procfs.h"
+#include "src/traced/probes/ftrace/proto_translation_table.h"
+
+namespace perfetto {
+namespace vendor_tracepoints {
+
+// Exposed for testing.
+std::vector<GroupAndName> DiscoverTracepoints(AtraceHalWrapper* hal,
+                                              FtraceProcfs* ftrace,
+                                              const std::string& category);
+
+// Returns a map from vendor category to events we should enable
+std::map<std::string, std::vector<GroupAndName>> DiscoverVendorTracepoints(
+    AtraceHalWrapper* hal,
+    FtraceProcfs* ftrace);
+
+}  // namespace vendor_tracepoints
+}  // namespace perfetto
+
+#endif  // SRC_TRACED_PROBES_FTRACE_DISCOVER_VENDOR_TRACEPOINTS_H_
diff --git a/src/traced/probes/ftrace/discover_vendor_tracepoints_unittest.cc b/src/traced/probes/ftrace/discover_vendor_tracepoints_unittest.cc
new file mode 100644
index 0000000..a859c15
--- /dev/null
+++ b/src/traced/probes/ftrace/discover_vendor_tracepoints_unittest.cc
@@ -0,0 +1,93 @@
+/*
+ * 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/traced/probes/ftrace/discover_vendor_tracepoints.h"
+
+#include <vector>
+
+#include "test/gtest_and_gmock.h"
+
+#include "src/traced/probes/ftrace/atrace_hal_wrapper.h"
+#include "src/traced/probes/ftrace/atrace_wrapper.h"
+#include "src/traced/probes/ftrace/ftrace_procfs.h"
+
+using testing::_;
+using testing::AnyNumber;
+using testing::ElementsAre;
+using testing::NiceMock;
+using testing::Return;
+using testing::Sequence;
+
+namespace perfetto {
+namespace vendor_tracepoints {
+namespace {
+
+class MockHal : public AtraceHalWrapper {
+ public:
+  MockHal() : AtraceHalWrapper() {}
+  MOCK_METHOD0(ListCategories, std::vector<std::string>());
+  MOCK_METHOD1(EnableCategories, bool(const std::vector<std::string>&));
+  MOCK_METHOD0(DisableAllCategories, bool());
+};
+
+class MockFtraceProcfs : public FtraceProcfs {
+ public:
+  MockFtraceProcfs() : FtraceProcfs("/root/") {
+    ON_CALL(*this, NumberOfCpus()).WillByDefault(Return(1));
+    ON_CALL(*this, WriteToFile(_, _)).WillByDefault(Return(true));
+    ON_CALL(*this, ClearFile(_)).WillByDefault(Return(true));
+    EXPECT_CALL(*this, NumberOfCpus()).Times(AnyNumber());
+  }
+
+  MOCK_METHOD2(WriteToFile,
+               bool(const std::string& path, const std::string& str));
+  MOCK_METHOD2(AppendToFile,
+               bool(const std::string& path, const std::string& str));
+  MOCK_METHOD1(ReadOneCharFromFile, char(const std::string& path));
+  MOCK_METHOD1(ClearFile, bool(const std::string& path));
+  MOCK_CONST_METHOD1(ReadFileIntoString, std::string(const std::string& path));
+  MOCK_METHOD0(ReadEnabledEvents, std::vector<std::string>());
+  MOCK_CONST_METHOD0(NumberOfCpus, size_t());
+  MOCK_CONST_METHOD1(GetEventNamesForGroup,
+                     const std::set<std::string>(const std::string& path));
+};
+
+TEST(DiscoverVendorTracepointsTest, DiscoverTracepointsTest) {
+  MockHal hal;
+  MockFtraceProcfs ftrace;
+  Sequence s;
+
+  EXPECT_CALL(ftrace, WriteToFile("/root/events/enable", "0"))
+      .InSequence(s)
+      .WillOnce(Return(true));
+  EXPECT_CALL(hal, EnableCategories(ElementsAre("gfx")))
+      .InSequence(s)
+      .WillOnce(Return(true));
+  EXPECT_CALL(ftrace, ReadEnabledEvents())
+      .InSequence(s)
+      .WillOnce(Return(std::vector<std::string>({"foo/bar", "a/b"})));
+  EXPECT_CALL(hal, DisableAllCategories()).InSequence(s).WillOnce(Return(true));
+  EXPECT_CALL(ftrace, WriteToFile("/root/events/enable", "0"))
+      .InSequence(s)
+      .WillOnce(Return(true));
+
+  EXPECT_THAT(DiscoverTracepoints(&hal, &ftrace, "gfx"),
+              ElementsAre(GroupAndName("foo", "bar"), GroupAndName("a", "b")));
+}
+
+}  // namespace
+}  // namespace vendor_tracepoints
+}  // namespace perfetto
diff --git a/src/traced/probes/ftrace/event_info.cc b/src/traced/probes/ftrace/event_info.cc
index b9976da..37a3736 100644
--- a/src/traced/probes/ftrace/event_info.cc
+++ b/src/traced/probes/ftrace/event_info.cc
@@ -4141,6 +4141,22 @@
        kUnsetFtraceId,
        34,
        kUnsetSize},
+      {"ion_stat",
+       "ion",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "buffer_id", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "len", 2, ProtoSchemaType::kInt64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "total_allocated", 3, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       335,
+       kUnsetSize},
       {"ipi_entry",
        "ipi",
        {
@@ -5472,6 +5488,16 @@
        kUnsetFtraceId,
        326,
        kUnsetSize},
+      {"mark_victim",
+       "oom",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "pid", 1, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       334,
+       kUnsetSize},
       {"cpu_frequency",
        "power",
        {
diff --git a/src/traced/probes/ftrace/format_parser.cc b/src/traced/probes/ftrace/format_parser.cc
index d4e119f..2be687d 100644
--- a/src/traced/probes/ftrace/format_parser.cc
+++ b/src/traced/probes/ftrace/format_parser.cc
@@ -72,8 +72,9 @@
       if (IsCommonFieldName(GetNameFromTypeAndName(type_and_name))) {
         if (common_fields)
           common_fields->push_back(field);
-      } else if (fields)
+      } else if (fields) {
         fields->push_back(field);
+      }
       continue;
     }
 
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer.cc b/src/traced/probes/ftrace/ftrace_config_muxer.cc
index f0cf926..144b804 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(
@@ -129,6 +152,7 @@
         events.insert(GroupAndName("mdss", "mdp_sspp_change"));
         events.insert(GroupAndName("mdss", "mdp_sspp_set"));
         AddEventGroup(table, "mali_systrace", &events);
+
         AddEventGroup(table, "sde", &events);
         events.insert(GroupAndName("sde", "tracing_mark_write"));
         events.insert(GroupAndName("sde", "sde_perf_update_bus"));
@@ -366,6 +390,7 @@
         events.insert(GroupAndName("kmem", "rss_stat"));
         events.insert(GroupAndName("kmem", "ion_heap_grow"));
         events.insert(GroupAndName("kmem", "ion_heap_shrink"));
+        events.insert(GroupAndName("mm_event", "mm_event_record"));
         continue;
       }
     }
@@ -394,9 +419,15 @@
   return pages;
 }
 
-FtraceConfigMuxer::FtraceConfigMuxer(FtraceProcfs* ftrace,
-                                     ProtoTranslationTable* table)
-    : ftrace_(ftrace), table_(table), current_state_(), ds_configs_() {}
+FtraceConfigMuxer::FtraceConfigMuxer(
+    FtraceProcfs* ftrace,
+    ProtoTranslationTable* table,
+    std::map<std::string, std::vector<GroupAndName>> vendor_events)
+    : ftrace_(ftrace),
+      table_(table),
+      current_state_(),
+      ds_configs_(),
+      vendor_events_(vendor_events) {}
 FtraceConfigMuxer::~FtraceConfigMuxer() = default;
 
 FtraceConfigId FtraceConfigMuxer::SetupConfig(const FtraceConfig& request) {
@@ -405,11 +436,11 @@
   if (ds_configs_.empty()) {
     PERFETTO_DCHECK(active_configs_.empty());
 
-    PERFETTO_DCHECK(!current_state_.tracing_on);
-
     // If someone outside of perfetto is using ftrace give up now.
-    if (is_ftrace_enabled)
+    if (is_ftrace_enabled) {
+      PERFETTO_ELOG("ftrace in use by non-Perfetto.");
       return 0;
+    }
 
     // Setup ftrace, without starting it. Setting buffers can be quite slow
     // (up to hundreds of ms).
@@ -417,12 +448,26 @@
     SetupBufferSize(request);
   } else {
     // Did someone turn ftrace off behind our back? If so give up.
-    if (!active_configs_.empty() && !is_ftrace_enabled)
+    if (!active_configs_.empty() && !is_ftrace_enabled) {
+      PERFETTO_ELOG("ftrace disabled by non-Perfetto.");
       return 0;
+    }
   }
 
   std::set<GroupAndName> events = GetFtraceEvents(request, table_);
 
+  // Vendors can provide a set of extra ftrace categories to be enabled when a
+  // specific atrace category is used (e.g. "gfx" -> ["my_hw/my_custom_event",
+  // "my_hw/my_special_gpu"]). Merge them with the hard coded events for each
+  // categories.
+  for (const std::string& category : request.atrace_categories()) {
+    if (vendor_events_.count(category)) {
+      for (const GroupAndName& event : vendor_events_[category]) {
+        events.insert(event);
+      }
+    }
+  }
+
   if (RequiresAtrace(request))
     UpdateAtrace(request);
 
@@ -453,10 +498,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;
 }
 
@@ -466,20 +515,19 @@
     return false;
   }
 
+  if (active_configs_.empty()) {
+    if (ftrace_->IsTracingEnabled()) {
+      // If someone outside of perfetto is using ftrace give up now.
+      PERFETTO_ELOG("ftrace in use by non-Perfetto.");
+      return false;
+    }
+    if (!ftrace_->EnableTracing()) {
+      PERFETTO_ELOG("Failed to enable ftrace.");
+      return false;
+    }
+  }
+
   active_configs_.insert(id);
-  if (active_configs_.size() > 1) {
-    PERFETTO_DCHECK(current_state_.tracing_on);
-    return true;  // We are not the first, ftrace is already enabled. All done.
-  }
-
-  PERFETTO_DCHECK(!current_state_.tracing_on);
-  if (ftrace_->IsTracingEnabled()) {
-    // If someone outside of perfetto is using ftrace give up now.
-    return false;
-  }
-
-  ftrace_->EnableTracing();
-  current_state_.tracing_on = true;
   return true;
 }
 
@@ -487,9 +535,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.
@@ -507,12 +575,11 @@
   // If there aren't any more active configs, disable ftrace.
   auto active_it = active_configs_.find(config_id);
   if (active_it != active_configs_.end()) {
-    PERFETTO_DCHECK(current_state_.tracing_on);
     active_configs_.erase(active_it);
     if (active_configs_.empty()) {
       // This was the last active config, disable ftrace.
-      ftrace_->DisableTracing();
-      current_state_.tracing_on = false;
+      if (!ftrace_->DisableTracing())
+        PERFETTO_ELOG("Failed to disable ftrace.");
     }
   }
 
@@ -524,8 +591,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 +645,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 +704,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..dc758ea 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.
@@ -59,7 +68,10 @@
  public:
   // The FtraceConfigMuxer and ProtoTranslationTable
   // should outlive this instance.
-  FtraceConfigMuxer(FtraceProcfs* ftrace, ProtoTranslationTable* table);
+  FtraceConfigMuxer(
+      FtraceProcfs* ftrace,
+      ProtoTranslationTable* table,
+      std::map<std::string, std::vector<GroupAndName>> vendor_events);
   virtual ~FtraceConfigMuxer();
 
   // Ask FtraceConfigMuxer to adjust ftrace procfs settings to
@@ -103,11 +115,16 @@
   }
 
  private:
+  static bool StartAtrace(const std::vector<std::string>& apps,
+                          const std::vector<std::string>& categories);
+
   struct FtraceState {
     EventFilter ftrace_events;
-    bool tracing_on = false;
-    bool atrace_on = false;
+    // 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 atrace_on = false;
   };
 
   FtraceConfigMuxer(const FtraceConfigMuxer&) = delete;
@@ -139,6 +156,8 @@
   // sizes and events, but don't enable ftrace (i.e. tracing_on).
   std::map<FtraceConfigId, FtraceDataSourceConfig> ds_configs_;
 
+  std::map<std::string, std::vector<GroupAndName>> vendor_events_;
+
   // Subset of |ds_configs_| that are currently active. At any time ftrace is
   // enabled iff |active_configs_| is not empty.
   std::set<FtraceConfigId> active_configs_;
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc b/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
index 890e07a..d9b5911 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
+++ b/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
@@ -197,7 +197,7 @@
 
   FtraceConfig config = CreateFtraceConfig({"power/cpu_frequency"});
 
-  FtraceConfigMuxer model(&ftrace, mock_table.get());
+  FtraceConfigMuxer model(&ftrace, mock_table.get(), {});
 
   ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
       .WillByDefault(Return("[local] global boot"));
@@ -244,7 +244,7 @@
 
   FtraceConfig config = CreateFtraceConfig({"group_one/foo", "group_two/foo"});
 
-  FtraceConfigMuxer model(&ftrace, mock_table.get());
+  FtraceConfigMuxer model(&ftrace, mock_table.get(), {});
 
   static constexpr int kEventId1 = 1;
   Event event1;
@@ -298,7 +298,7 @@
   EXPECT_CALL(ftrace,
               WriteToFile("/root/events/sched/sched_new_event/enable", "1"));
 
-  FtraceConfigMuxer model(&ftrace, mock_table.get());
+  FtraceConfigMuxer model(&ftrace, mock_table.get(), {});
   std::set<std::string> n = {"sched_switch", "sched_new_event"};
   ON_CALL(ftrace, GetEventNamesForGroup("events/sched"))
       .WillByDefault(Return(n));
@@ -346,7 +346,7 @@
 
   FtraceConfig config = CreateFtraceConfig({"group_one/*", "group_two/*"});
 
-  FtraceConfigMuxer model(&ftrace, mock_table.get());
+  FtraceConfigMuxer model(&ftrace, mock_table.get(), {});
 
   std::set<std::string> event_names = {"foo"};
   ON_CALL(ftrace, GetEventNamesForGroup("events/group_one"))
@@ -395,7 +395,7 @@
 
   FtraceConfig config = CreateFtraceConfig({"sched_switch", "foo"});
 
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
       .WillByDefault(Return("[local] global boot"));
@@ -445,7 +445,7 @@
 
   FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
 
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   // If someone is using ftrace already don't stomp on what they are doing.
   EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
@@ -461,7 +461,7 @@
   FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
   *config.add_atrace_categories() = "sched";
 
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
       .WillOnce(Return('0'));
@@ -501,7 +501,7 @@
   *config.add_atrace_apps() = "com.google.android.gms.persistent";
   *config.add_atrace_apps() = "com.google.android.gms";
 
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
       .WillOnce(Return('0'));
@@ -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,11 +526,216 @@
   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;
 
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   EXPECT_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
       .Times(AnyNumber());
@@ -556,7 +761,7 @@
 
 TEST_F(FtraceConfigMuxerTest, GetFtraceEvents) {
   MockFtraceProcfs ftrace;
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
   std::set<GroupAndName> events =
@@ -568,7 +773,7 @@
 
 TEST_F(FtraceConfigMuxerTest, GetFtraceEventsAtrace) {
   MockFtraceProcfs ftrace;
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   FtraceConfig config = CreateFtraceConfig({});
   *config.add_atrace_categories() = "sched";
@@ -582,7 +787,7 @@
 
 TEST_F(FtraceConfigMuxerTest, GetFtraceEventsAtraceCategories) {
   MockFtraceProcfs ftrace;
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   FtraceConfig config = CreateFtraceConfig({});
   *config.add_atrace_categories() = "sched";
@@ -606,7 +811,7 @@
   MockFtraceProcfs ftrace;
   FtraceConfig config =
       CreateFtraceConfig({"sched/sched_switch", "cgroup/cgroup_mkdir"});
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
       .WillByDefault(Return("[local] global boot"));
@@ -667,7 +872,7 @@
 
   NiceMock<MockFtraceProcfs> ftrace;
   table_ = CreateFakeTable(valid_compact_format);
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   // First data source - request compact encoding.
   FtraceConfig config_enabled = CreateFtraceConfig({"sched/sched_switch"});
@@ -698,7 +903,7 @@
 
 TEST_F(FtraceConfigMuxerTest, CompactSchedConfigWithInvalidFormat) {
   NiceMock<MockFtraceProcfs> ftrace;
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   // Request compact encoding.
   FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
diff --git a/src/traced/probes/ftrace/ftrace_controller.cc b/src/traced/probes/ftrace/ftrace_controller.cc
index a66ed05..da88e3d 100644
--- a/src/traced/probes/ftrace/ftrace_controller.cc
+++ b/src/traced/probes/ftrace/ftrace_controller.cc
@@ -34,8 +34,10 @@
 #include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/metatrace.h"
 #include "perfetto/ext/tracing/core/trace_writer.h"
+#include "src/traced/probes/ftrace/atrace_hal_wrapper.h"
 #include "src/traced/probes/ftrace/cpu_reader.h"
 #include "src/traced/probes/ftrace/cpu_stats_parser.h"
+#include "src/traced/probes/ftrace/discover_vendor_tracepoints.h"
 #include "src/traced/probes/ftrace/event_info.h"
 #include "src/traced/probes/ftrace/ftrace_config_muxer.h"
 #include "src/traced/probes/ftrace/ftrace_data_source.h"
@@ -105,6 +107,8 @@
 // We don't know what state the rest of the system and process is so as far
 // as possible avoid allocations.
 void HardResetFtraceState() {
+  PERFETTO_LOG("Hard resetting ftrace state.");
+
   WriteToFile("/sys/kernel/debug/tracing/tracing_on", "0");
   WriteToFile("/sys/kernel/debug/tracing/buffer_size_kb", "4");
   WriteToFile("/sys/kernel/debug/tracing/events/enable", "0");
@@ -136,8 +140,12 @@
   if (!table)
     return nullptr;
 
+  AtraceHalWrapper hal;
+  auto vendor_evts =
+      vendor_tracepoints::DiscoverVendorTracepoints(&hal, ftrace_procfs.get());
+
   std::unique_ptr<FtraceConfigMuxer> model = std::unique_ptr<FtraceConfigMuxer>(
-      new FtraceConfigMuxer(ftrace_procfs.get(), table.get()));
+      new FtraceConfigMuxer(ftrace_procfs.get(), table.get(), vendor_evts));
   return std::unique_ptr<FtraceController>(
       new FtraceController(std::move(ftrace_procfs), std::move(table),
                            std::move(model), runner, observer));
diff --git a/src/traced/probes/ftrace/ftrace_controller_unittest.cc b/src/traced/probes/ftrace/ftrace_controller_unittest.cc
index 1f40c83..dca24c8 100644
--- a/src/traced/probes/ftrace/ftrace_controller_unittest.cc
+++ b/src/traced/probes/ftrace/ftrace_controller_unittest.cc
@@ -93,7 +93,7 @@
 std::unique_ptr<FtraceConfigMuxer> FakeModel(FtraceProcfs* ftrace,
                                              ProtoTranslationTable* table) {
   return std::unique_ptr<FtraceConfigMuxer>(
-      new FtraceConfigMuxer(ftrace, table));
+      new FtraceConfigMuxer(ftrace, table, {}));
 }
 
 class MockFtraceProcfs : public FtraceProcfs {
diff --git a/src/traced/probes/ftrace/ftrace_data_source.cc b/src/traced/probes/ftrace/ftrace_data_source.cc
index f28a491..a801e05 100644
--- a/src/traced/probes/ftrace/ftrace_data_source.cc
+++ b/src/traced/probes/ftrace/ftrace_data_source.cc
@@ -26,14 +26,17 @@
 namespace perfetto {
 
 // static
-constexpr int FtraceDataSource::kTypeId;
+const ProbesDataSource::Descriptor FtraceDataSource::descriptor = {
+    /*name*/ "linux.ftrace",
+    /*flags*/ Descriptor::kFlagsNone,
+};
 
 FtraceDataSource::FtraceDataSource(
     base::WeakPtr<FtraceController> controller_weak,
     TracingSessionID session_id,
     const FtraceConfig& config,
     std::unique_ptr<TraceWriter> writer)
-    : ProbesDataSource(session_id, kTypeId),
+    : ProbesDataSource(session_id, &descriptor),
       config_(config),
       writer_(std::move(writer)),
       controller_weak_(std::move(controller_weak)) {}
diff --git a/src/traced/probes/ftrace/ftrace_data_source.h b/src/traced/probes/ftrace/ftrace_data_source.h
index 1730616..9534763 100644
--- a/src/traced/probes/ftrace/ftrace_data_source.h
+++ b/src/traced/probes/ftrace/ftrace_data_source.h
@@ -54,7 +54,8 @@
 // FtraceController.
 class FtraceDataSource : public ProbesDataSource {
  public:
-  static constexpr int kTypeId = 1;
+  static const ProbesDataSource::Descriptor descriptor;
+
   FtraceDataSource(base::WeakPtr<FtraceController>,
                    TracingSessionID,
                    const FtraceConfig&,
diff --git a/src/traced/probes/ftrace/ftrace_procfs.cc b/src/traced/probes/ftrace/ftrace_procfs.cc
index 060f4cd..480d303 100644
--- a/src/traced/probes/ftrace/ftrace_procfs.cc
+++ b/src/traced/probes/ftrace/ftrace_procfs.cc
@@ -27,6 +27,8 @@
 
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/utils.h"
 
 namespace perfetto {
@@ -107,6 +109,20 @@
   return ReadFileIntoString(path);
 }
 
+std::vector<std::string> FtraceProcfs::ReadEnabledEvents() {
+  std::string path = root_ + "set_event";
+  std::string s = ReadFileIntoString(path);
+  base::StringSplitter ss(s, '\n');
+  std::vector<std::string> events;
+  while (ss.Next()) {
+    std::string event = ss.cur_token();
+    if (event.size() == 0)
+      continue;
+    events.push_back(base::StripChars(event, ":", '/'));
+  }
+  return events;
+}
+
 std::string FtraceProcfs::ReadPageHeaderFormat() const {
   std::string path = root_ + "events/header_page";
   return ReadFileIntoString(path);
@@ -157,12 +173,14 @@
 
 bool FtraceProcfs::EnableTracing() {
   KernelLogWrite("perfetto: enabled ftrace\n");
+  PERFETTO_LOG("enabled ftrace");
   std::string path = root_ + "tracing_on";
   return WriteToFile(path, "1");
 }
 
 bool FtraceProcfs::DisableTracing() {
   KernelLogWrite("perfetto: disabled ftrace\n");
+  PERFETTO_LOG("disabled ftrace");
   std::string path = root_ + "tracing_on";
   return WriteToFile(path, "0");
 }
@@ -173,7 +191,10 @@
 
 bool FtraceProcfs::IsTracingEnabled() {
   std::string path = root_ + "tracing_on";
-  return ReadOneCharFromFile(path) == '1';
+  char tracing_on = ReadOneCharFromFile(path);
+  if (tracing_on == '\0')
+    PERFETTO_PLOG("Failed to read %s", path.c_str());
+  return tracing_on == '1';
 }
 
 bool FtraceProcfs::SetClock(const std::string& clock_name) {
diff --git a/src/traced/probes/ftrace/ftrace_procfs.h b/src/traced/probes/ftrace/ftrace_procfs.h
index cd05adb..3d8186f 100644
--- a/src/traced/probes/ftrace/ftrace_procfs.h
+++ b/src/traced/probes/ftrace/ftrace_procfs.h
@@ -20,6 +20,7 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <vector>
 
 #include "perfetto/ext/base/scoped_file.h"
 
@@ -91,6 +92,9 @@
   // Get all the available clocks.
   std::set<std::string> AvailableClocks();
 
+  // Get all the enabled events.
+  virtual std::vector<std::string> ReadEnabledEvents();
+
   // Open the raw pipe for |cpu|.
   virtual base::ScopedFile OpenPipeForCpu(size_t cpu);
 
@@ -98,7 +102,7 @@
       const std::string& path) const;
 
  protected:
-  // virtual and public for testing.
+  // virtual and protected for testing.
   virtual bool WriteToFile(const std::string& path, const std::string& str);
   virtual bool AppendToFile(const std::string& path, const std::string& str);
   virtual bool ClearFile(const std::string& path);
diff --git a/src/traced/probes/ftrace/ftrace_procfs_integrationtest.cc b/src/traced/probes/ftrace/ftrace_procfs_integrationtest.cc
index b8bf269..8634d88 100644
--- a/src/traced/probes/ftrace/ftrace_procfs_integrationtest.cc
+++ b/src/traced/probes/ftrace/ftrace_procfs_integrationtest.cc
@@ -24,9 +24,27 @@
 #include "src/traced/probes/ftrace/ftrace_procfs.h"
 #include "test/gtest_and_gmock.h"
 
-using testing::HasSubstr;
-using testing::Not;
 using testing::Contains;
+using testing::HasSubstr;
+using testing::IsEmpty;
+using testing::Not;
+using testing::UnorderedElementsAre;
+
+// These tests run only on Android because on linux they require access to
+// ftrace, which would be problematic in the CI when multiple tests run
+// concurrently on the same machine. Android instead uses one emulator instance
+// for each worker.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+// On Android these tests conflict with traced_probes which expects to be the
+// only one modifying tracing. This led to the Setup code which attempts to
+// to skip these tests when traced_probes is using tracing. Unfortunately this
+// is racey and we still see spurious failures in practice. For now disable
+// these tests on Android also.
+// TODO(b/150675975) Re-enable these tests.
+#define ANDROID_ONLY_TEST(x) DISABLED_##x
+#else
+#define ANDROID_ONLY_TEST(x) DISABLED_##x
+#endif
 
 namespace perfetto {
 namespace {
@@ -39,12 +57,6 @@
   return std::string(FtraceController::kTracingPaths[i]);
 }
 
-void ResetFtrace(FtraceProcfs* ftrace) {
-  ftrace->DisableAllEvents();
-  ftrace->ClearTrace();
-  ftrace->EnableTracing();
-}
-
 std::string ReadFile(const std::string& name) {
   std::string result;
   PERFETTO_CHECK(base::ReadFile(GetFtracePath() + name, &result));
@@ -59,161 +71,111 @@
   return output;
 }
 
-}  // namespace
+class FtraceProcfsIntegrationTest : public testing::Test {
+ public:
+  void SetUp() override;
+  void TearDown() override;
 
-// TODO(lalitm): reenable these tests (see b/72306171).
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#define MAYBE_CreateWithGoodPath CreateWithGoodPath
-#else
-#define MAYBE_CreateWithGoodPath DISABLED_CreateWithGoodPath
-#endif
-TEST(FtraceProcfsIntegrationTest, MAYBE_CreateWithGoodPath) {
-  EXPECT_TRUE(FtraceProcfs::Create(GetFtracePath()));
+  std::unique_ptr<FtraceProcfs> ftrace_;
+};
+
+void FtraceProcfsIntegrationTest::SetUp() {
+  ftrace_ = FtraceProcfs::Create(GetFtracePath());
+  ASSERT_TRUE(ftrace_);
+  if (ftrace_->IsTracingEnabled()) {
+    GTEST_SKIP() << "Something else is using ftrace, skipping";
+  }
+
+  ftrace_->DisableAllEvents();
+  ftrace_->ClearTrace();
+  ftrace_->EnableTracing();
 }
 
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#define MAYBE_CreateWithBadPath CreateWithBadPath
-#else
-#define MAYBE_CreateWithBadPath DISABLED_CreateWithBadath
-#endif
-TEST(FtraceProcfsIntegrationTest, MAYBE_CreateWithBadPath) {
+void FtraceProcfsIntegrationTest::TearDown() {
+  ftrace_->DisableAllEvents();
+  ftrace_->ClearTrace();
+  ftrace_->DisableTracing();
+}
+
+TEST_F(FtraceProcfsIntegrationTest, ANDROID_ONLY_TEST(CreateWithBadPath)) {
   EXPECT_FALSE(FtraceProcfs::Create(GetFtracePath() + std::string("bad_path")));
 }
 
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#define MAYBE_ClearTrace ClearTrace
-#else
-#define MAYBE_ClearTrace DISABLED_ClearTrace
-#endif
-TEST(FtraceProcfsIntegrationTest, MAYBE_ClearTrace) {
-  FtraceProcfs ftrace(GetFtracePath());
-  ResetFtrace(&ftrace);
-  ftrace.WriteTraceMarker("Hello, World!");
-  ftrace.ClearTrace();
+TEST_F(FtraceProcfsIntegrationTest, ANDROID_ONLY_TEST(ClearTrace)) {
+  ftrace_->WriteTraceMarker("Hello, World!");
+  ftrace_->ClearTrace();
   EXPECT_THAT(GetTraceOutput(), Not(HasSubstr("Hello, World!")));
 }
 
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#define MAYBE_TraceMarker TraceMarker
-#else
-#define MAYBE_TraceMarker DISABLED_TraceMarker
-#endif
-TEST(FtraceProcfsIntegrationTest, MAYBE_TraceMarker) {
-  FtraceProcfs ftrace(GetFtracePath());
-  ResetFtrace(&ftrace);
-  ftrace.WriteTraceMarker("Hello, World!");
+TEST_F(FtraceProcfsIntegrationTest, ANDROID_ONLY_TEST(TraceMarker)) {
+  ftrace_->WriteTraceMarker("Hello, World!");
   EXPECT_THAT(GetTraceOutput(), HasSubstr("Hello, World!"));
 }
 
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#define MAYBE_EnableDisableEvent EnableDisableEvent
-#else
-#define MAYBE_EnableDisableEvent DISABLED_EnableDisableEvent
-#endif
-TEST(FtraceProcfsIntegrationTest, MAYBE_EnableDisableEvent) {
-  FtraceProcfs ftrace(GetFtracePath());
-  ResetFtrace(&ftrace);
-  ftrace.EnableEvent("sched", "sched_switch");
+TEST_F(FtraceProcfsIntegrationTest, ANDROID_ONLY_TEST(EnableDisableEvent)) {
+  ASSERT_TRUE(ftrace_->EnableEvent("sched", "sched_switch"));
   sleep(1);
+  ASSERT_TRUE(ftrace_->DisableEvent("sched", "sched_switch"));
+
   EXPECT_THAT(GetTraceOutput(), HasSubstr("sched_switch"));
 
-  ftrace.DisableEvent("sched", "sched_switch");
-  ftrace.ClearTrace();
+  ftrace_->ClearTrace();
   sleep(1);
   EXPECT_THAT(GetTraceOutput(), Not(HasSubstr("sched_switch")));
 }
 
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#define MAYBE_EnableDisableTracing EnableDisableTracing
-#else
-#define MAYBE_EnableDisableTracing DISABLED_EnableDisableTracing
-#endif
-TEST(FtraceProcfsIntegrationTest, MAYBE_EnableDisableTracing) {
-  FtraceProcfs ftrace(GetFtracePath());
-  ResetFtrace(&ftrace);
-  EXPECT_TRUE(ftrace.IsTracingEnabled());
-  ftrace.WriteTraceMarker("Before");
-  ftrace.DisableTracing();
-  EXPECT_FALSE(ftrace.IsTracingEnabled());
-  ftrace.WriteTraceMarker("During");
-  ftrace.EnableTracing();
-  EXPECT_TRUE(ftrace.IsTracingEnabled());
-  ftrace.WriteTraceMarker("After");
+TEST_F(FtraceProcfsIntegrationTest, ANDROID_ONLY_TEST(EnableDisableTracing)) {
+  EXPECT_TRUE(ftrace_->IsTracingEnabled());
+  ftrace_->WriteTraceMarker("Before");
+  ftrace_->DisableTracing();
+  EXPECT_FALSE(ftrace_->IsTracingEnabled());
+  ftrace_->WriteTraceMarker("During");
+  ftrace_->EnableTracing();
+  EXPECT_TRUE(ftrace_->IsTracingEnabled());
+  ftrace_->WriteTraceMarker("After");
   EXPECT_THAT(GetTraceOutput(), HasSubstr("Before"));
   EXPECT_THAT(GetTraceOutput(), Not(HasSubstr("During")));
   EXPECT_THAT(GetTraceOutput(), HasSubstr("After"));
 }
 
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#define MAYBE_ReadFormatFile ReadFormatFile
-#else
-#define MAYBE_ReadFormatFile DISABLED_ReadFormatFile
-#endif
-TEST(FtraceProcfsIntegrationTest, MAYBE_ReadFormatFile) {
-  FtraceProcfs ftrace(GetFtracePath());
-  std::string format = ftrace.ReadEventFormat("ftrace", "print");
+TEST_F(FtraceProcfsIntegrationTest, ANDROID_ONLY_TEST(ReadFormatFile)) {
+  std::string format = ftrace_->ReadEventFormat("ftrace", "print");
   EXPECT_THAT(format, HasSubstr("name: print"));
   EXPECT_THAT(format, HasSubstr("field:char buf"));
 }
 
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#define MAYBE_CanOpenTracePipeRaw CanOpenTracePipeRaw
-#else
-#define MAYBE_CanOpenTracePipeRaw DISABLED_CanOpenTracePipeRaw
-#endif
-TEST(FtraceProcfsIntegrationTest, MAYBE_CanOpenTracePipeRaw) {
-  FtraceProcfs ftrace(GetFtracePath());
-  EXPECT_TRUE(ftrace.OpenPipeForCpu(0));
+TEST_F(FtraceProcfsIntegrationTest, ANDROID_ONLY_TEST(CanOpenTracePipeRaw)) {
+  EXPECT_TRUE(ftrace_->OpenPipeForCpu(0));
 }
 
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#define MAYBE_Clock Clock
-#else
-#define MAYBE_Clock DISABLED_Clock
-#endif
-TEST(FtraceProcfsIntegrationTest, MAYBE_Clock) {
-  FtraceProcfs ftrace(GetFtracePath());
-  std::set<std::string> clocks = ftrace.AvailableClocks();
+TEST_F(FtraceProcfsIntegrationTest, ANDROID_ONLY_TEST(Clock)) {
+  std::set<std::string> clocks = ftrace_->AvailableClocks();
   EXPECT_THAT(clocks, Contains("local"));
   EXPECT_THAT(clocks, Contains("global"));
 
-  EXPECT_TRUE(ftrace.SetClock("global"));
-  EXPECT_EQ(ftrace.GetClock(), "global");
-  EXPECT_TRUE(ftrace.SetClock("local"));
-  EXPECT_EQ(ftrace.GetClock(), "local");
+  EXPECT_TRUE(ftrace_->SetClock("global"));
+  EXPECT_EQ(ftrace_->GetClock(), "global");
+  EXPECT_TRUE(ftrace_->SetClock("local"));
+  EXPECT_EQ(ftrace_->GetClock(), "local");
 }
 
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#define MAYBE_CanSetBufferSize CanSetBufferSize
-#else
-#define MAYBE_CanSetBufferSize DISABLED_CanSetBufferSize
-#endif
-TEST(FtraceProcfsIntegrationTest, MAYBE_CanSetBufferSize) {
-  FtraceProcfs ftrace(GetFtracePath());
-  EXPECT_TRUE(ftrace.SetCpuBufferSizeInPages(4ul));
+TEST_F(FtraceProcfsIntegrationTest, ANDROID_ONLY_TEST(CanSetBufferSize)) {
+  EXPECT_TRUE(ftrace_->SetCpuBufferSizeInPages(4ul));
   EXPECT_EQ(ReadFile("buffer_size_kb"), "16\n");  // (4096 * 4) / 1024
-  EXPECT_TRUE(ftrace.SetCpuBufferSizeInPages(5ul));
+  EXPECT_TRUE(ftrace_->SetCpuBufferSizeInPages(5ul));
   EXPECT_EQ(ReadFile("buffer_size_kb"), "20\n");  // (4096 * 5) / 1024
 }
 
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#define MAYBE_FtraceControllerHardReset FtraceControllerHardReset
-#else
-#define MAYBE_FtraceControllerHardReset DISABLED_FtraceControllerHardReset
-#endif
-TEST(FtraceProcfsIntegrationTest, MAYBE_FtraceControllerHardReset) {
-  FtraceProcfs ftrace(GetFtracePath());
-  ResetFtrace(&ftrace);
-
-  ftrace.SetCpuBufferSizeInPages(4ul);
-  ftrace.EnableTracing();
-  ftrace.EnableEvent("sched", "sched_switch");
-  ftrace.WriteTraceMarker("Hello, World!");
+TEST_F(FtraceProcfsIntegrationTest,
+       ANDROID_ONLY_TEST(FtraceControllerHardReset)) {
+  ftrace_->SetCpuBufferSizeInPages(4ul);
+  ftrace_->EnableTracing();
+  ftrace_->EnableEvent("sched", "sched_switch");
+  ftrace_->WriteTraceMarker("Hello, World!");
 
   EXPECT_EQ(ReadFile("buffer_size_kb"), "16\n");
   EXPECT_EQ(ReadFile("tracing_on"), "1\n");
   EXPECT_EQ(ReadFile("events/enable"), "X\n");
-  EXPECT_THAT(GetTraceOutput(), HasSubstr("Hello"));
 
   HardResetFtraceState();
 
@@ -223,4 +185,20 @@
   EXPECT_THAT(GetTraceOutput(), Not(HasSubstr("Hello")));
 }
 
+TEST_F(FtraceProcfsIntegrationTest, ANDROID_ONLY_TEST(ReadEnabledEvents)) {
+  EXPECT_THAT(ftrace_->ReadEnabledEvents(), IsEmpty());
+
+  ftrace_->EnableEvent("sched", "sched_switch");
+  ftrace_->EnableEvent("kmem", "kmalloc");
+
+  EXPECT_THAT(ftrace_->ReadEnabledEvents(),
+              UnorderedElementsAre("sched/sched_switch", "kmem/kmalloc"));
+
+  ftrace_->DisableEvent("sched", "sched_switch");
+  ftrace_->DisableEvent("kmem", "kmalloc");
+
+  EXPECT_THAT(ftrace_->ReadEnabledEvents(), IsEmpty());
+}
+
+}  // namespace
 }  // namespace perfetto
diff --git a/src/traced/probes/ftrace/kallsyms/BUILD.gn b/src/traced/probes/ftrace/kallsyms/BUILD.gn
index c4e6ca7..0c42b98 100644
--- a/src/traced/probes/ftrace/kallsyms/BUILD.gn
+++ b/src/traced/probes/ftrace/kallsyms/BUILD.gn
@@ -34,9 +34,7 @@
     "../../../../../gn:gtest_and_gmock",
     "../../../../base",
   ]
-  sources = [
-    "kernel_symbol_map_unittest.cc",
-  ]
+  sources = [ "kernel_symbol_map_unittest.cc" ]
 }
 
 if (enable_perfetto_benchmarks) {
@@ -49,8 +47,6 @@
       "../../../../base",
       "../../../../base:test_support",
     ]
-    sources = [
-      "kernel_symbol_map_benchmark.cc",
-    ]
+    sources = [ "kernel_symbol_map_benchmark.cc" ]
   }
 }
diff --git a/src/traced/probes/ftrace/proto_translation_table.h b/src/traced/probes/ftrace/proto_translation_table.h
index f9c5be6..83dd19d 100644
--- a/src/traced/probes/ftrace/proto_translation_table.h
+++ b/src/traced/probes/ftrace/proto_translation_table.h
@@ -19,6 +19,7 @@
 
 #include <stdint.h>
 
+#include <iostream>
 #include <map>
 #include <memory>
 #include <set>
@@ -65,6 +66,10 @@
   std::string name_;
 };
 
+inline void PrintTo(const GroupAndName& event, ::std::ostream* os) {
+  *os << "GroupAndName(" << event.group() << ", " << event.name() << ")";
+}
+
 bool InferFtraceType(const std::string& type_and_name,
                      size_t size,
                      bool is_signed,
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/ion/ion_stat/format b/src/traced/probes/ftrace/test/data/synthetic/events/ion/ion_stat/format
new file mode 100644
index 0000000..a326934
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/ion/ion_stat/format
@@ -0,0 +1,13 @@
+name: ion_stat
+ID: 1076
+format:
+	field:unsigned short common_type;	offset:0;	size:2;	signed:0;
+	field:unsigned char common_flags;	offset:2;	size:1;	signed:0;
+	field:unsigned char common_preempt_count;	offset:3;	size:1;	signed:0;
+	field:int common_pid;	offset:4;	size:4;	signed:1;
+
+	field:unsigned int buffer_id;	offset:8;	size:4;	signed:0;
+	field:long len;	offset:16;	size:8;	signed:1;
+	field:unsigned long total_allocated;	offset:24;	size:8;	signed:0;
+
+print fmt: "buffer_id=%u len=%ldB total_allocated=%ldB", REC->buffer_id, REC->len, REC->total_allocated
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/oom/mark_victim/format b/src/traced/probes/ftrace/test/data/synthetic/events/oom/mark_victim/format
new file mode 100644
index 0000000..1715681
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/oom/mark_victim/format
@@ -0,0 +1,11 @@
+name: mark_victim
+ID: 195
+format:
+	field:unsigned short common_type;	offset:0;	size:2;	signed:0;
+	field:unsigned char common_flags;	offset:2;	size:1;	signed:0;
+	field:unsigned char common_preempt_count;	offset:3;	size:1;	signed:0;
+	field:int common_pid;	offset:4;	size:4;	signed:1;
+
+	field:int pid;	offset:8;	size:4;	signed:1;
+
+print fmt: "pid=%d", REC->pid
diff --git a/src/traced/probes/initial_display_state/BUILD.gn b/src/traced/probes/initial_display_state/BUILD.gn
new file mode 100644
index 0000000..ea71625
--- /dev/null
+++ b/src/traced/probes/initial_display_state/BUILD.gn
@@ -0,0 +1,45 @@
+# 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/test.gni")
+
+source_set("initial_display_state") {
+  public_deps = [ "../../../tracing/core" ]
+  deps = [
+    "..:data_source",
+    "../../../../gn:default_deps",
+    "../../../../include/perfetto/ext/traced",
+    "../../../../protos/perfetto/trace:zero",
+    "../../../../protos/perfetto/trace/android:zero",
+    "../../../base",
+    "../common",
+  ]
+  sources = [
+    "initial_display_state_data_source.cc",
+    "initial_display_state_data_source.h",
+  ]
+}
+
+perfetto_unittest_source_set("unittests") {
+  testonly = true
+  deps = [
+    ":initial_display_state",
+    "../../../../gn:default_deps",
+    "../../../../gn:gtest_and_gmock",
+    "../../../../protos/perfetto/trace/android:cpp",
+    "../../../../src/tracing/test:test_support",
+    "../common:test_support",
+  ]
+  sources = [ "initial_display_state_data_source_unittest.cc" ]
+}
diff --git a/src/traced/probes/initial_display_state/initial_display_state_data_source.cc b/src/traced/probes/initial_display_state/initial_display_state_data_source.cc
new file mode 100644
index 0000000..5d3150e
--- /dev/null
+++ b/src/traced/probes/initial_display_state/initial_display_state_data_source.cc
@@ -0,0 +1,93 @@
+/*
+ * 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/traced/probes/initial_display_state/initial_display_state_data_source.h"
+
+#include "perfetto/base/time.h"
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/string_utils.h"
+
+#include "protos/perfetto/trace/android/initial_display_state.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <sys/system_properties.h>
+#endif
+
+namespace perfetto {
+
+// static
+const InitialDisplayStateDataSource::Descriptor
+    InitialDisplayStateDataSource::descriptor = {
+        /* name */ "android.polled_state",
+        /* flags */ Descriptor::kFlagsNone,
+};
+
+InitialDisplayStateDataSource::InitialDisplayStateDataSource(
+    TracingSessionID session_id,
+    std::unique_ptr<TraceWriter> writer)
+    : ProbesDataSource(session_id, &descriptor), writer_(std::move(writer)) {}
+
+void InitialDisplayStateDataSource::Start() {
+  auto packet = writer_->NewTracePacket();
+  packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
+  const base::Optional<std::string> screen_state_str =
+      ReadProperty("debug.tracing.screen_state");
+  const base::Optional<std::string> screen_brightness_str =
+      ReadProperty("debug.tracing.screen_brightness");
+  const base::Optional<int> screen_state =
+      screen_state_str ? base::StringToInt32(*screen_state_str) : base::nullopt;
+  const base::Optional<double> screen_brightness =
+      screen_brightness_str ? base::StringToDouble(*screen_brightness_str)
+                            : base::nullopt;
+  if (screen_state || screen_brightness) {
+    auto* state = packet->set_initial_display_state();
+    if (screen_state) {
+      state->set_display_state(*screen_state);
+    }
+    if (screen_brightness) {
+      state->set_brightness(*screen_brightness);
+    }
+  }
+  packet->Finalize();
+  writer_->Flush();
+}
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+const base::Optional<std::string> InitialDisplayStateDataSource::ReadProperty(
+    const std::string name) {
+  char value[PROP_VALUE_MAX];
+  if (__system_property_get(name.c_str(), value)) {
+    return base::make_optional(value);
+  } else {
+    PERFETTO_ELOG("Unable to read %s", name.c_str());
+    return base::nullopt;
+  }
+}
+#else
+const base::Optional<std::string> InitialDisplayStateDataSource::ReadProperty(
+    const std::string name __attribute__((unused))) {
+  PERFETTO_ELOG("Initial display state only supported on Android.");
+  return base::nullopt;
+}
+#endif
+
+void InitialDisplayStateDataSource::Flush(FlushRequestID,
+                                          std::function<void()> callback) {
+  writer_->Flush(callback);
+}
+
+}  // namespace perfetto
diff --git a/src/traced/probes/initial_display_state/initial_display_state_data_source.h b/src/traced/probes/initial_display_state/initial_display_state_data_source.h
new file mode 100644
index 0000000..c3746d2
--- /dev/null
+++ b/src/traced/probes/initial_display_state/initial_display_state_data_source.h
@@ -0,0 +1,49 @@
+/*
+ * 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_TRACED_PROBES_INITIAL_DISPLAY_STATE_INITIAL_DISPLAY_STATE_DATA_SOURCE_H_
+#define SRC_TRACED_PROBES_INITIAL_DISPLAY_STATE_INITIAL_DISPLAY_STATE_DATA_SOURCE_H_
+
+#include <memory>
+
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/tracing/core/trace_writer.h"
+#include "src/traced/probes/probes_data_source.h"
+
+namespace perfetto {
+
+class InitialDisplayStateDataSource : public ProbesDataSource {
+ public:
+  static const ProbesDataSource::Descriptor descriptor;
+
+  InitialDisplayStateDataSource(TracingSessionID,
+                                std::unique_ptr<TraceWriter> writer);
+
+  // ProbesDataSource implementation.
+  void Start() override;
+  void Flush(FlushRequestID, std::function<void()> callback) override;
+
+  // Virtual for testing.
+  virtual const base::Optional<std::string> ReadProperty(
+      const std::string name);
+
+ private:
+  std::unique_ptr<TraceWriter> writer_;
+};
+
+}  // namespace perfetto
+
+#endif  // SRC_TRACED_PROBES_INITIAL_DISPLAY_STATE_INITIAL_DISPLAY_STATE_DATA_SOURCE_H_
diff --git a/src/traced/probes/initial_display_state/initial_display_state_data_source_unittest.cc b/src/traced/probes/initial_display_state/initial_display_state_data_source_unittest.cc
new file mode 100644
index 0000000..a4f4f57
--- /dev/null
+++ b/src/traced/probes/initial_display_state/initial_display_state_data_source_unittest.cc
@@ -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.
+ */
+
+#include "src/traced/probes/initial_display_state/initial_display_state_data_source.h"
+#include "src/tracing/core/trace_writer_for_testing.h"
+#include "test/gtest_and_gmock.h"
+
+#include "protos/perfetto/trace/android/initial_display_state.gen.h"
+#include "protos/perfetto/trace/trace_packet.gen.h"
+
+using ::testing::AnyOf;
+using ::testing::ElementsAre;
+using ::testing::Return;
+
+namespace perfetto {
+namespace {
+
+class TestInitialDisplayStateDataSource : public InitialDisplayStateDataSource {
+ public:
+  TestInitialDisplayStateDataSource(std::unique_ptr<TraceWriter> writer)
+      : InitialDisplayStateDataSource(
+            /* session_id */ 0,
+            std::move(writer)) {}
+
+  MOCK_METHOD1(ReadProperty,
+               const base::Optional<std::string>(const std::string));
+};
+
+class InitialDisplayStateDataSourceTest : public ::testing::Test {
+ protected:
+  std::unique_ptr<TestInitialDisplayStateDataSource>
+  GetInitialDisplayStateDataSource() {
+    auto writer =
+        std::unique_ptr<TraceWriterForTesting>(new TraceWriterForTesting());
+    writer_raw_ = writer.get();
+    auto instance = std::unique_ptr<TestInitialDisplayStateDataSource>(
+        new TestInitialDisplayStateDataSource(std::move(writer)));
+    return instance;
+  }
+
+  TraceWriterForTesting* writer_raw_ = nullptr;
+};
+
+TEST_F(InitialDisplayStateDataSourceTest, Success) {
+  ASSERT_TRUE(true);
+  auto data_source = GetInitialDisplayStateDataSource();
+  EXPECT_CALL(*data_source, ReadProperty("debug.tracing.screen_state"))
+      .WillOnce(Return(base::make_optional("2")));
+  EXPECT_CALL(*data_source, ReadProperty("debug.tracing.screen_brightness"))
+      .WillOnce(Return(base::make_optional("0.123456")));
+  data_source->Start();
+
+  protos::gen::TracePacket packet = writer_raw_->GetOnlyTracePacket();
+  ASSERT_TRUE(packet.has_initial_display_state());
+  auto state = packet.initial_display_state();
+  ASSERT_EQ(state.display_state(), 2);
+  ASSERT_EQ(state.brightness(), 0.123456);
+}
+
+TEST_F(InitialDisplayStateDataSourceTest, Invalid) {
+  ASSERT_TRUE(true);
+  auto data_source = GetInitialDisplayStateDataSource();
+  EXPECT_CALL(*data_source, ReadProperty("debug.tracing.screen_state"))
+      .WillOnce(Return(base::make_optional("2")));
+  EXPECT_CALL(*data_source, ReadProperty("debug.tracing.screen_brightness"))
+      .WillOnce(Return(base::make_optional("gotta wear shades")));
+  data_source->Start();
+
+  protos::gen::TracePacket packet = writer_raw_->GetOnlyTracePacket();
+  ASSERT_TRUE(packet.has_initial_display_state());
+  auto state = packet.initial_display_state();
+  ASSERT_EQ(state.display_state(), 2);
+  ASSERT_FALSE(state.has_brightness());
+}
+
+TEST_F(InitialDisplayStateDataSourceTest, Failure) {
+  ASSERT_TRUE(true);
+  auto data_source = GetInitialDisplayStateDataSource();
+  EXPECT_CALL(*data_source, ReadProperty("debug.tracing.screen_state"))
+      .WillOnce(Return(base::nullopt));
+  EXPECT_CALL(*data_source, ReadProperty("debug.tracing.screen_brightness"))
+      .WillOnce(Return(base::nullopt));
+  data_source->Start();
+
+  protos::gen::TracePacket packet = writer_raw_->GetOnlyTracePacket();
+  ASSERT_FALSE(packet.has_initial_display_state());
+}
+
+}  // namespace
+}  // namespace perfetto
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/metatrace/metatrace_data_source.cc b/src/traced/probes/metatrace/metatrace_data_source.cc
index f87117e..1c5a2a7 100644
--- a/src/traced/probes/metatrace/metatrace_data_source.cc
+++ b/src/traced/probes/metatrace/metatrace_data_source.cc
@@ -29,13 +29,15 @@
 namespace perfetto {
 
 // static
-const char* MetatraceDataSource::kDataSourceName =
-    MetatraceWriter::kDataSourceName;
+const ProbesDataSource::Descriptor MetatraceDataSource::descriptor = {
+    /*name*/ MetatraceWriter::kDataSourceName,
+    /*flags*/ Descriptor::kFlagsNone,
+};
 
 MetatraceDataSource::MetatraceDataSource(base::TaskRunner* task_runner,
                                          TracingSessionID session_id,
                                          std::unique_ptr<TraceWriter> writer)
-    : ProbesDataSource(session_id, kTypeId),
+    : ProbesDataSource(session_id, &descriptor),
       task_runner_(task_runner),
       trace_writer_(std::move(writer)) {}
 
diff --git a/src/traced/probes/metatrace/metatrace_data_source.h b/src/traced/probes/metatrace/metatrace_data_source.h
index 3bd0d04..d721eda 100644
--- a/src/traced/probes/metatrace/metatrace_data_source.h
+++ b/src/traced/probes/metatrace/metatrace_data_source.h
@@ -32,8 +32,7 @@
 
 class MetatraceDataSource : public ProbesDataSource {
  public:
-  static constexpr int kTypeId = 8;
-  static const char* kDataSourceName;
+  static const ProbesDataSource::Descriptor descriptor;
 
   MetatraceDataSource(base::TaskRunner*,
                       TracingSessionID,
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/packages_list/packages_list_data_source.cc b/src/traced/probes/packages_list/packages_list_data_source.cc
index 730d0f6..68fa662 100644
--- a/src/traced/probes/packages_list/packages_list_data_source.cc
+++ b/src/traced/probes/packages_list/packages_list_data_source.cc
@@ -26,6 +26,12 @@
 
 namespace perfetto {
 
+// static
+const ProbesDataSource::Descriptor PackagesListDataSource::descriptor = {
+    /*name*/ "android.packages_list",
+    /*flags*/ Descriptor::kFlagsNone,
+};
+
 bool ParsePackagesListStream(protos::pbzero::PackagesList* packages_list_packet,
                              const base::ScopedFstream& fs,
                              const std::set<std::string>& package_name_filter) {
@@ -109,7 +115,7 @@
     const DataSourceConfig& ds_config,
     TracingSessionID session_id,
     std::unique_ptr<TraceWriter> writer)
-    : ProbesDataSource(session_id, kTypeId), writer_(std::move(writer)) {
+    : ProbesDataSource(session_id, &descriptor), writer_(std::move(writer)) {
   PackagesListConfig::Decoder cfg(ds_config.packages_list_config_raw());
   for (auto name = cfg.package_name_filter(); name; ++name) {
     package_name_filter_.emplace((*name).ToStdString());
diff --git a/src/traced/probes/packages_list/packages_list_data_source.h b/src/traced/probes/packages_list/packages_list_data_source.h
index 6f0ee9a..5dd3fdc 100644
--- a/src/traced/probes/packages_list/packages_list_data_source.h
+++ b/src/traced/probes/packages_list/packages_list_data_source.h
@@ -50,7 +50,8 @@
 
 class PackagesListDataSource : public ProbesDataSource {
  public:
-  static constexpr int kTypeId = 7;
+  static const ProbesDataSource::Descriptor descriptor;
+
   PackagesListDataSource(const DataSourceConfig& ds_config,
                          TracingSessionID session_id,
                          std::unique_ptr<TraceWriter> writer);
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/power/android_power_data_source.cc b/src/traced/probes/power/android_power_data_source.cc
index 98bf2de..0b4e749 100644
--- a/src/traced/probes/power/android_power_data_source.cc
+++ b/src/traced/probes/power/android_power_data_source.cc
@@ -38,10 +38,16 @@
 namespace perfetto {
 
 namespace {
-constexpr uint32_t kMinPollRateMs = 250;
+constexpr uint32_t kMinPollIntervalMs = 100;
 constexpr size_t kMaxNumRails = 32;
 }  // namespace
 
+// static
+const ProbesDataSource::Descriptor AndroidPowerDataSource::descriptor = {
+    /*name*/ "android.power",
+    /*flags*/ Descriptor::kFlagsNone,
+};
+
 // Dynamically loads the libperfetto_android_internal.so library which
 // allows to proxy calls to android hwbinder in in-tree builds.
 struct AndroidPowerDataSource::DynamicLibLoader {
@@ -88,21 +94,21 @@
     base::TaskRunner* task_runner,
     TracingSessionID session_id,
     std::unique_ptr<TraceWriter> writer)
-    : ProbesDataSource(session_id, kTypeId),
+    : ProbesDataSource(session_id, &descriptor),
       task_runner_(task_runner),
       rail_descriptors_logged_(false),
       writer_(std::move(writer)),
       weak_factory_(this) {
   using protos::pbzero::AndroidPowerConfig;
   AndroidPowerConfig::Decoder pcfg(cfg.android_power_config_raw());
-  poll_rate_ms_ = pcfg.battery_poll_ms();
+  poll_interval_ms_ = pcfg.battery_poll_ms();
   rails_collection_enabled_ = pcfg.collect_power_rails();
 
-  if (poll_rate_ms_ < kMinPollRateMs) {
+  if (poll_interval_ms_ < kMinPollIntervalMs) {
     PERFETTO_ELOG("Battery poll interval of %" PRIu32
                   " ms is too low. Capping to %" PRIu32 " ms",
-                  poll_rate_ms_, kMinPollRateMs);
-    poll_rate_ms_ = kMinPollRateMs;
+                  poll_interval_ms_, kMinPollIntervalMs);
+    poll_interval_ms_ = kMinPollIntervalMs;
   }
   for (auto counter = pcfg.battery_counters(); counter; ++counter) {
     auto hal_id = android_internal::BatteryCounter::kUnspecified;
@@ -143,7 +149,7 @@
         if (weak_this)
           weak_this->Tick();
       },
-      poll_rate_ms_ - (now_ms % poll_rate_ms_));
+      poll_interval_ms_ - (now_ms % poll_interval_ms_));
 
   WriteBatteryCounters();
   WritePowerRailsData();
@@ -209,11 +215,11 @@
     }
 
     for (const auto& rail_descriptor : rail_descriptors) {
-      auto* descriptor = rails_proto->add_rail_descriptor();
-      descriptor->set_index(rail_descriptor.index);
-      descriptor->set_rail_name(rail_descriptor.rail_name);
-      descriptor->set_subsys_name(rail_descriptor.subsys_name);
-      descriptor->set_sampling_rate(rail_descriptor.sampling_rate);
+      auto* rail_desc_proto = rails_proto->add_rail_descriptor();
+      rail_desc_proto->set_index(rail_descriptor.index);
+      rail_desc_proto->set_rail_name(rail_descriptor.rail_name);
+      rail_desc_proto->set_subsys_name(rail_descriptor.subsys_name);
+      rail_desc_proto->set_sampling_rate(rail_descriptor.sampling_rate);
     }
   }
 
diff --git a/src/traced/probes/power/android_power_data_source.h b/src/traced/probes/power/android_power_data_source.h
index e178fc5..0977d11 100644
--- a/src/traced/probes/power/android_power_data_source.h
+++ b/src/traced/probes/power/android_power_data_source.h
@@ -34,7 +34,7 @@
 
 class AndroidPowerDataSource : public ProbesDataSource {
  public:
-  static constexpr int kTypeId = 5;
+  static const ProbesDataSource::Descriptor descriptor;
 
   AndroidPowerDataSource(DataSourceConfig,
                          base::TaskRunner*,
@@ -57,7 +57,7 @@
   void WritePowerRailsData();
 
   base::TaskRunner* const task_runner_;
-  uint32_t poll_rate_ms_ = 0;
+  uint32_t poll_interval_ms_ = 0;
   std::bitset<8> counters_enabled_;
   bool rails_collection_enabled_;
   bool rail_descriptors_logged_;
diff --git a/src/traced/probes/probes_data_source.cc b/src/traced/probes/probes_data_source.cc
index f051f8d..b214a20 100644
--- a/src/traced/probes/probes_data_source.cc
+++ b/src/traced/probes/probes_data_source.cc
@@ -18,8 +18,10 @@
 
 namespace perfetto {
 
-ProbesDataSource::ProbesDataSource(TracingSessionID session_id, int id)
-    : tracing_session_id(session_id), type_id(id) {}
+ProbesDataSource::ProbesDataSource(TracingSessionID session_id,
+                                   const Descriptor* desc)
+    : tracing_session_id(session_id), descriptor(desc) {}
+
 ProbesDataSource::~ProbesDataSource() = default;
 
 }  // namespace perfetto
diff --git a/src/traced/probes/probes_data_source.h b/src/traced/probes/probes_data_source.h
index ef410c7..6ceef56 100644
--- a/src/traced/probes/probes_data_source.h
+++ b/src/traced/probes/probes_data_source.h
@@ -21,14 +21,25 @@
 
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/tracing/core/basic_types.h"
+#include "perfetto/tracing/core/forward_decls.h"
 
 namespace perfetto {
 
 // Base class for all data sources in traced_probes.
 class ProbesDataSource {
  public:
-  // |type_id| is a home-brewed RTTI, e.g. InodeFileDataSource::kTypeId.
-  ProbesDataSource(TracingSessionID, int type_id);
+  // Static properties for a data source. Needs to be available before
+  // instantiating each data source. It must have static lifetime.
+  struct Descriptor {
+    enum Flags : uint32_t {
+      kFlagsNone = 0,
+      kHandlesIncrementalState = 1 << 0,
+    };
+    const char* const name;
+    uint32_t flags;
+  };
+
+  ProbesDataSource(TracingSessionID, const Descriptor*);
   virtual ~ProbesDataSource();
 
   virtual void Start() = 0;
@@ -43,7 +54,7 @@
   }
 
   const TracingSessionID tracing_session_id;
-  const int type_id;
+  const Descriptor* const descriptor;
   bool started = false;  // Set by probes_producer.cc.
 
  private:
diff --git a/src/traced/probes/probes_producer.cc b/src/traced/probes/probes_producer.cc
index 90238b5..b1bfe5e 100644
--- a/src/traced/probes/probes_producer.cc
+++ b/src/traced/probes/probes_producer.cc
@@ -34,14 +34,17 @@
 #include "perfetto/tracing/core/data_source_descriptor.h"
 #include "perfetto/tracing/core/trace_config.h"
 #include "src/traced/probes/android_log/android_log_data_source.h"
+#include "src/traced/probes/common/cpu_freq_info.h"
 #include "src/traced/probes/filesystem/inode_file_data_source.h"
 #include "src/traced/probes/ftrace/ftrace_data_source.h"
+#include "src/traced/probes/initial_display_state/initial_display_state_data_source.h"
 #include "src/traced/probes/metatrace/metatrace_data_source.h"
 #include "src/traced/probes/packages_list/packages_list_data_source.h"
 #include "src/traced/probes/power/android_power_data_source.h"
 #include "src/traced/probes/probes_data_source.h"
 #include "src/traced/probes/ps/process_stats_data_source.h"
 #include "src/traced/probes/sys_stats/sys_stats_data_source.h"
+#include "src/traced/probes/system_info/system_info_data_source.h"
 
 #include "protos/perfetto/config/ftrace/ftrace_config.gen.h"
 #include "protos/perfetto/trace/filesystem/inode_file_map.pbzero.h"
@@ -58,15 +61,22 @@
 // Should be larger than FtraceController::kControllerFlushTimeoutMs.
 constexpr uint32_t kFlushTimeoutMs = 1000;
 
-constexpr char kFtraceSourceName[] = "linux.ftrace";
-constexpr char kProcessStatsSourceName[] = "linux.process_stats";
-constexpr char kInodeMapSourceName[] = "linux.inode_file_map";
-constexpr char kSysStatsSourceName[] = "linux.sys_stats";
-constexpr char kAndroidPowerSourceName[] = "android.power";
-constexpr char kAndroidLogSourceName[] = "android.log";
-constexpr char kPackagesListSourceName[] = "android.packages_list";
+constexpr size_t kTracingSharedMemSizeHintBytes = 1024 * 1024;
+constexpr size_t kTracingSharedMemPageSizeHintBytes = 32 * 1024;
 
-}  // namespace.
+ProbesDataSource::Descriptor const* const kAllDataSources[]{
+    &FtraceDataSource::descriptor,               //
+    &ProcessStatsDataSource::descriptor,         //
+    &InodeFileDataSource::descriptor,            //
+    &SysStatsDataSource::descriptor,             //
+    &AndroidPowerDataSource::descriptor,         //
+    &AndroidLogDataSource::descriptor,           //
+    &PackagesListDataSource::descriptor,         //
+    &MetatraceDataSource::descriptor,            //
+    &SystemInfoDataSource::descriptor,           //
+    &InitialDisplayStateDataSource::descriptor,  //
+};
+}  // namespace
 
 // State transition diagram:
 //                    +----------------------------+
@@ -89,54 +99,16 @@
   ResetConnectionBackoff();
   PERFETTO_LOG("Connected to the service");
 
-  {
-    DataSourceDescriptor desc;
-    desc.set_name(kFtraceSourceName);
-    endpoint_->RegisterDataSource(desc);
-  }
-
-  {
-    DataSourceDescriptor desc;
-    desc.set_name(kProcessStatsSourceName);
-    desc.set_handles_incremental_state_clear(true);
-    endpoint_->RegisterDataSource(desc);
-  }
-
-  {
-    DataSourceDescriptor desc;
-    desc.set_name(kInodeMapSourceName);
-    endpoint_->RegisterDataSource(desc);
-  }
-
-  {
-    DataSourceDescriptor desc;
-    desc.set_name(kSysStatsSourceName);
-    endpoint_->RegisterDataSource(desc);
-  }
-
-  {
-    DataSourceDescriptor desc;
-    desc.set_name(kAndroidPowerSourceName);
-    endpoint_->RegisterDataSource(desc);
-  }
-
-  {
-    DataSourceDescriptor desc;
-    desc.set_name(kAndroidLogSourceName);
-    endpoint_->RegisterDataSource(desc);
-  }
-
-  {
-    DataSourceDescriptor desc;
-    desc.set_name(kPackagesListSourceName);
-    endpoint_->RegisterDataSource(desc);
-  }
-
-  {
-    DataSourceDescriptor desc;
-    desc.set_name(MetatraceDataSource::kDataSourceName);
-    desc.set_will_notify_on_stop(true);
-    endpoint_->RegisterDataSource(desc);
+  // Register all the data sources.
+  for (const FtraceDataSource::Descriptor* desc : kAllDataSources) {
+    DataSourceDescriptor proto_desc;
+    proto_desc.set_name(desc->name);
+    proto_desc.set_will_notify_on_start(true);
+    proto_desc.set_will_notify_on_stop(true);
+    using Flags = ProbesDataSource::Descriptor::Flags;
+    if (desc->flags & Flags::kHandlesIncrementalState)
+      proto_desc.set_handles_incremental_state_clear(true);
+    endpoint_->RegisterDataSource(proto_desc);
   }
 }
 
@@ -178,22 +150,26 @@
   PERFETTO_CHECK(session_id > 0);
 
   std::unique_ptr<ProbesDataSource> data_source;
-  if (config.name() == kFtraceSourceName) {
+  if (config.name() == FtraceDataSource::descriptor.name) {
     data_source = CreateFtraceDataSource(session_id, config);
-  } else if (config.name() == kInodeMapSourceName) {
+  } else if (config.name() == InodeFileDataSource::descriptor.name) {
     data_source = CreateInodeFileDataSource(session_id, config);
-  } else if (config.name() == kProcessStatsSourceName) {
+  } else if (config.name() == ProcessStatsDataSource::descriptor.name) {
     data_source = CreateProcessStatsDataSource(session_id, config);
-  } else if (config.name() == kSysStatsSourceName) {
+  } else if (config.name() == SysStatsDataSource::descriptor.name) {
     data_source = CreateSysStatsDataSource(session_id, config);
-  } else if (config.name() == kAndroidPowerSourceName) {
+  } else if (config.name() == AndroidPowerDataSource::descriptor.name) {
     data_source = CreateAndroidPowerDataSource(session_id, config);
-  } else if (config.name() == kAndroidLogSourceName) {
+  } else if (config.name() == AndroidLogDataSource::descriptor.name) {
     data_source = CreateAndroidLogDataSource(session_id, config);
-  } else if (config.name() == kPackagesListSourceName) {
+  } else if (config.name() == PackagesListDataSource::descriptor.name) {
     data_source = CreatePackagesListDataSource(session_id, config);
-  } else if (config.name() == MetatraceDataSource::kDataSourceName) {
+  } else if (config.name() == MetatraceDataSource::descriptor.name) {
     data_source = CreateMetatraceDataSource(session_id, config);
+  } else if (config.name() == SystemInfoDataSource::descriptor.name) {
+    data_source = CreateSystemInfoDataSource(session_id, config);
+  } else if (config.name() == InitialDisplayStateDataSource::descriptor.name) {
+    data_source = CreateInitialDisplayStateDataSource(session_id, config);
   }
 
   if (!data_source) {
@@ -225,6 +201,7 @@
   }
   data_source->started = true;
   data_source->Start();
+  endpoint_->NotifyDataSourceStarted(instance_id);
 }
 
 std::unique_ptr<ProbesDataSource> ProbesProducer::CreateFtraceDataSource(
@@ -258,9 +235,7 @@
       ftrace_->GetWeakPtr(), session_id, std::move(ftrace_config),
       endpoint_->CreateTraceWriter(buffer_id)));
   if (!ftrace_->AddDataSource(data_source.get())) {
-    PERFETTO_ELOG(
-        "Failed to setup tracing (too many concurrent sessions or ftrace is "
-        "already in use)");
+    PERFETTO_ELOG("Failed to setup ftrace");
     return nullptr;
   }
   return std::unique_ptr<ProbesDataSource>(std::move(data_source));
@@ -284,8 +259,8 @@
     const DataSourceConfig& config) {
   auto buffer_id = static_cast<BufferID>(config.target_buffer());
   return std::unique_ptr<ProcessStatsDataSource>(new ProcessStatsDataSource(
-      task_runner_, session_id, endpoint_->CreateTraceWriter(buffer_id),
-      config));
+      task_runner_, session_id, endpoint_->CreateTraceWriter(buffer_id), config,
+      std::unique_ptr<CpuFreqInfo>(new CpuFreqInfo())));
 }
 
 std::unique_ptr<ProbesDataSource> ProbesProducer::CreateAndroidPowerDataSource(
@@ -331,6 +306,24 @@
       task_runner_, session_id, endpoint_->CreateTraceWriter(buffer_id)));
 }
 
+std::unique_ptr<ProbesDataSource> ProbesProducer::CreateSystemInfoDataSource(
+    TracingSessionID session_id,
+    const DataSourceConfig& config) {
+  auto buffer_id = static_cast<BufferID>(config.target_buffer());
+  return std::unique_ptr<ProbesDataSource>(new SystemInfoDataSource(
+      session_id, endpoint_->CreateTraceWriter(buffer_id),
+      std::unique_ptr<CpuFreqInfo>(new CpuFreqInfo())));
+}
+
+std::unique_ptr<ProbesDataSource>
+ProbesProducer::CreateInitialDisplayStateDataSource(
+    TracingSessionID session_id,
+    const DataSourceConfig& config) {
+  auto buffer_id = static_cast<BufferID>(config.target_buffer());
+  return std::unique_ptr<ProbesDataSource>(new InitialDisplayStateDataSource(
+      session_id, endpoint_->CreateTraceWriter(buffer_id)));
+}
+
 void ProbesProducer::StopDataSource(DataSourceInstanceID id) {
   PERFETTO_LOG("Producer stop (id=%" PRIu64 ")", id);
   auto it = data_sources_.find(id);
@@ -343,10 +336,10 @@
 
   // MetatraceDataSource special case: re-flush and ack the stop (to record the
   // flushes of other data sources).
-  if (data_source->type_id == MetatraceDataSource::kTypeId) {
+  if (data_source->descriptor == &MetatraceDataSource::descriptor)
     data_source->Flush(FlushRequestID{0}, [] {});
-    endpoint_->NotifyDataSourceStopped(id);
-  }
+
+  endpoint_->NotifyDataSourceStopped(id);
 
   TracingSessionID session_id = data_source->tracing_session_id;
   auto range = session_data_sources_.equal_range(session_id);
@@ -485,32 +478,21 @@
     ProbesDataSource* ds = it->second;
     if (!ds->started)
       continue;
-    switch (ds->type_id) {
-      case FtraceDataSource::kTypeId:
-        metadata = static_cast<FtraceDataSource*>(ds)->mutable_metadata();
-        break;
-      case InodeFileDataSource::kTypeId:
-        inode_data_source = static_cast<InodeFileDataSource*>(ds);
-        break;
-      case ProcessStatsDataSource::kTypeId: {
-        // A trace session might have declared more than one ps data source.
-        // In those cases we often use one for a full dump on startup (
-        // targeting a dedicated buffer) and another one for on-demand dumps
-        // targeting the main buffer.
-        // Only use the one that has on-demand dumps enabled, if any.
-        auto ps = static_cast<ProcessStatsDataSource*>(ds);
-        if (ps->on_demand_dumps_enabled())
-          ps_data_source = ps;
-        break;
-      }
-      case SysStatsDataSource::kTypeId:
-      case AndroidLogDataSource::kTypeId:
-      case PackagesListDataSource::kTypeId:
-      case MetatraceDataSource::kTypeId:
-        break;
-      default:
-        PERFETTO_DFATAL("Invalid data source.");
-    }  // switch (type_id)
+
+    if (ds->descriptor == &FtraceDataSource::descriptor) {
+      metadata = static_cast<FtraceDataSource*>(ds)->mutable_metadata();
+    } else if (ds->descriptor == &InodeFileDataSource::descriptor) {
+      inode_data_source = static_cast<InodeFileDataSource*>(ds);
+    } else if (ds->descriptor == &ProcessStatsDataSource::descriptor) {
+      // A trace session might have declared more than one ps data source.
+      // In those cases we often use one for a full dump on startup (
+      // targeting a dedicated buffer) and another one for on-demand dumps
+      // targeting the main buffer.
+      // Only use the one that has on-demand dumps enabled, if any.
+      auto ps = static_cast<ProcessStatsDataSource*>(ds);
+      if (ps->on_demand_dumps_enabled())
+        ps_data_source = ps;
+    }
   }    // for (session_data_sources_)
 }
 
@@ -529,7 +511,9 @@
   PERFETTO_DCHECK(state_ == kNotConnected);
   state_ = kConnecting;
   endpoint_ = ProducerIPCClient::Connect(
-      socket_name_, this, "perfetto.traced_probes", task_runner_);
+      socket_name_, this, "perfetto.traced_probes", task_runner_,
+      TracingService::ProducerSMBScrapingMode::kDisabled,
+      kTracingSharedMemSizeHintBytes, kTracingSharedMemPageSizeHintBytes);
 }
 
 void ProbesProducer::IncreaseConnectionBackoff() {
diff --git a/src/traced/probes/probes_producer.h b/src/traced/probes/probes_producer.h
index 1cdbd28..39d6425 100644
--- a/src/traced/probes/probes_producer.h
+++ b/src/traced/probes/probes_producer.h
@@ -87,6 +87,12 @@
   std::unique_ptr<ProbesDataSource> CreateMetatraceDataSource(
       TracingSessionID session_id,
       const DataSourceConfig& config);
+  std::unique_ptr<ProbesDataSource> CreateSystemInfoDataSource(
+      TracingSessionID session_id,
+      const DataSourceConfig& config);
+  std::unique_ptr<ProbesDataSource> CreateInitialDisplayStateDataSource(
+      TracingSessionID session_id,
+      const DataSourceConfig& config);
 
  private:
   enum State {
diff --git a/src/traced/probes/ps/BUILD.gn b/src/traced/probes/ps/BUILD.gn
index 85c24bc..93f44ab 100644
--- a/src/traced/probes/ps/BUILD.gn
+++ b/src/traced/probes/ps/BUILD.gn
@@ -15,16 +15,16 @@
 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",
+    "../common",
   ]
   sources = [
     "process_stats_data_source.cc",
@@ -41,9 +41,8 @@
     "../../../../protos/perfetto/config/process_stats:cpp",
     "../../../../protos/perfetto/trace/ps:cpp",
     "../../../../src/base:test_support",
-    "../../../../src/tracing:test_support",
+    "../../../../src/tracing/test:test_support",
+    "../common: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 511aa23..ddbc152 100644
--- a/src/traced/probes/ps/process_stats_data_source.cc
+++ b/src/traced/probes/ps/process_stats_data_source.cc
@@ -27,6 +27,7 @@
 #include "perfetto/ext/base/metatrace.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/ext/base/string_utils.h"
 #include "perfetto/tracing/core/data_source_config.h"
 
 #include "protos/perfetto/config/process_stats/process_stats_config.pbzero.h"
@@ -48,6 +49,10 @@
 
 namespace {
 
+// Default upper bound on the number of thread cpu frequency keys, used if none
+// was provided in the config. The cache is trimmed if it exceeds this size.
+const size_t kThreadTimeInStateCacheSize = 10000;
+
 inline int32_t ParseIntValue(const char* str) {
   int32_t ret = 0;
   for (;;) {
@@ -84,21 +89,31 @@
 }  // namespace
 
 // static
-constexpr int ProcessStatsDataSource::kTypeId;
+const ProbesDataSource::Descriptor ProcessStatsDataSource::descriptor = {
+    /*name*/ "linux.process_stats",
+    /*flags*/ Descriptor::kHandlesIncrementalState,
+};
 
 ProcessStatsDataSource::ProcessStatsDataSource(
     base::TaskRunner* task_runner,
     TracingSessionID session_id,
     std::unique_ptr<TraceWriter> writer,
-    const DataSourceConfig& ds_config)
-    : ProbesDataSource(session_id, kTypeId),
+    const DataSourceConfig& ds_config,
+    std::unique_ptr<CpuFreqInfo> cpu_freq_info)
+    : ProbesDataSource(session_id, &descriptor),
       task_runner_(task_runner),
       writer_(std::move(writer)),
+      cpu_freq_info_(std::move(cpu_freq_info)),
       weak_factory_(this) {
   using protos::pbzero::ProcessStatsConfig;
   ProcessStatsConfig::Decoder cfg(ds_config.process_stats_config_raw());
   record_thread_names_ = cfg.record_thread_names();
   dump_all_procs_on_start_ = cfg.scan_all_processes_on_start();
+  record_thread_time_in_state_ = cfg.record_thread_time_in_state();
+  thread_time_in_state_cache_size_ = cfg.thread_time_in_state_cache_size();
+  if (thread_time_in_state_cache_size_ == 0)
+    thread_time_in_state_cache_size_ = kThreadTimeInStateCacheSize;
+
   enable_on_demand_dumps_ = true;
   for (auto quirk = cfg.quirks(); quirk; ++quirk) {
     if (*quirk == ProcessStatsConfig::DISABLE_ON_DEMAND)
@@ -282,6 +297,12 @@
   return contents;
 }
 
+base::ScopedDir ProcessStatsDataSource::OpenProcTaskDir(int32_t pid) {
+  char task_path[255];
+  sprintf(task_path, "/proc/%d/task", pid);
+  return base::ScopedDir(opendir(task_path));
+}
+
 std::string ProcessStatsDataSource::ReadProcStatusEntry(const std::string& buf,
                                                         const char* key) {
   auto begin = buf.find(key);
@@ -368,6 +389,7 @@
   if (++thiz.cache_ticks_ == thiz.process_stats_cache_ttl_ticks_) {
     thiz.cache_ticks_ = 0;
     thiz.process_stats_cache_.clear();
+    thiz.thread_time_in_state_cache_.clear();
   }
 }
 
@@ -413,6 +435,13 @@
       }
     }
 
+    if (record_thread_time_in_state_) {
+      if (auto task_dir = OpenProcTaskDir(pid)) {
+        while (int32_t tid = ReadNextNumericDir(*task_dir))
+          WriteThreadStats(pid, tid);
+      }
+    }
+
     pids.insert(pid);
   }
   FinalizeCurPacket();
@@ -420,6 +449,15 @@
   // Ensure that we write once long-term process info (e.g., name) for new pids
   // that we haven't seen before.
   WriteProcessTree(pids);
+
+  // Ensure the cache stays within bounds by erasing some entries.
+  while (thread_time_in_state_cache_.size() >
+         thread_time_in_state_cache_size_) {
+    auto random = thread_time_in_state_cache_.begin();
+    std::advance(random, rand() % static_cast<int32_t>(
+                                      thread_time_in_state_cache_.size()));
+    thread_time_in_state_cache_.erase(random);
+  }
 }
 
 // Returns true if the stats for the given |pid| have been written, false it
@@ -533,6 +571,54 @@
   return proc_status_has_mem_counters;
 }
 
+void ProcessStatsDataSource::WriteThreadStats(int32_t pid, int32_t tid) {
+  // Reads /proc/tid/time_in_state, which looks like:
+  // cpu0
+  // 100 0
+  // 200 5
+  // ...
+  // cpu6
+  // 200 0
+  // 300 70
+  // ...
+  // Pairs of CPU frequency and the number of ticks at that frequency.
+  std::string time_in_state = ReadProcPidFile(tid, "time_in_state");
+  protos::pbzero::ProcessStats_Thread* thread = nullptr;
+  base::StringSplitter entries(std::move(time_in_state), '\n');
+  uint32_t last_cpu = 0;
+  while (entries.Next()) {
+    std::string line(entries.cur_token());
+    if (base::StartsWith(line, "cpu")) {
+      last_cpu = base::StringToUInt32(line.substr(3)).value();
+      continue;
+    }
+    base::StringSplitter key_value(&entries, ' ');
+    if (!key_value.Next())
+      continue;
+    uint32_t freq = ToU32(key_value.cur_token());
+    uint32_t freq_index = cpu_freq_info_->GetCpuFreqIndex(last_cpu, freq);
+    TidCpuFreqIndex key = {tid, freq_index};
+    if (!key_value.Next())
+      continue;
+    auto maybe_ticks = base::CStringToUInt64(key_value.cur_token());
+    if (!maybe_ticks.has_value())
+      continue;
+    uint64_t ticks = maybe_ticks.value();
+    if (ticks == 0)
+      continue;
+    auto& cached_ticks = thread_time_in_state_cache_[key];
+    if (ticks != cached_ticks) {
+      if (thread == nullptr) {
+        thread = GetOrCreateStatsProcess(pid)->add_threads();
+        thread->set_tid(tid);
+      }
+      thread->add_cpu_freq_indices(freq_index);
+      thread->add_cpu_freq_ticks(ticks);
+      cached_ticks = ticks;
+    }
+  }
+}
+
 uint64_t ProcessStatsDataSource::CacheProcFsScanStartTimestamp() {
   if (!cur_procfs_scan_start_timestamp_)
     cur_procfs_scan_start_timestamp_ =
@@ -547,6 +633,7 @@
 
   cache_ticks_ = 0;
   process_stats_cache_.clear();
+  thread_time_in_state_cache_.clear();
 
   // Set the relevant flag in the next packet.
   did_clear_incremental_state_ = true;
diff --git a/src/traced/probes/ps/process_stats_data_source.h b/src/traced/probes/ps/process_stats_data_source.h
index 100ef62..19a9d4a 100644
--- a/src/traced/probes/ps/process_stats_data_source.h
+++ b/src/traced/probes/ps/process_stats_data_source.h
@@ -18,6 +18,7 @@
 #define SRC_TRACED_PROBES_PS_PROCESS_STATS_DATA_SOURCE_H_
 
 #include <limits>
+#include <map>
 #include <memory>
 #include <set>
 #include <unordered_map>
@@ -29,6 +30,7 @@
 #include "perfetto/ext/tracing/core/basic_types.h"
 #include "perfetto/ext/tracing/core/trace_writer.h"
 #include "perfetto/tracing/core/forward_decls.h"
+#include "src/traced/probes/common/cpu_freq_info.h"
 #include "src/traced/probes/probes_data_source.h"
 
 namespace perfetto {
@@ -48,12 +50,13 @@
 
 class ProcessStatsDataSource : public ProbesDataSource {
  public:
-  static constexpr int kTypeId = 3;
+  static const ProbesDataSource::Descriptor descriptor;
 
   ProcessStatsDataSource(base::TaskRunner*,
                          TracingSessionID,
                          std::unique_ptr<TraceWriter> writer,
-                         const DataSourceConfig&);
+                         const DataSourceConfig&,
+                         std::unique_ptr<CpuFreqInfo> cpu_freq_info);
   ~ProcessStatsDataSource() override;
 
   base::WeakPtr<ProcessStatsDataSource> GetWeakPtr() const;
@@ -71,6 +74,7 @@
   // Virtual for testing.
   virtual base::ScopedDir OpenProcDir();
   virtual std::string ReadProcPidFile(int32_t pid, const std::string& file);
+  virtual base::ScopedDir OpenProcTaskDir(int32_t pid);
 
  private:
   struct CachedProcessStats {
@@ -105,6 +109,7 @@
   static void Tick(base::WeakPtr<ProcessStatsDataSource>);
   void WriteAllProcessStats();
   bool WriteMemCounters(int32_t pid, const std::string& proc_status);
+  void WriteThreadStats(int32_t pid, int32_t tid);
 
   // Scans /proc/pid/status and writes the ProcessTree packet for input pids.
   void WriteProcessTree(const base::FlatSet<int32_t>&);
@@ -134,6 +139,7 @@
   bool record_thread_names_ = false;
   bool enable_on_demand_dumps_ = true;
   bool dump_all_procs_on_start_ = false;
+  bool record_thread_time_in_state_ = false;
 
   // This set contains PIDs as per the Linux kernel notion of a PID (which is
   // really a TID). In practice this set will contain all TIDs for all processes
@@ -152,6 +158,13 @@
   uint32_t process_stats_cache_ttl_ticks_ = 0;
   std::unordered_map<int32_t, CachedProcessStats> process_stats_cache_;
 
+  using TidCpuFreqIndex =
+      std::tuple</* tid */ int32_t, /* cpu_freq_index */ uint32_t>;
+  std::map<TidCpuFreqIndex, uint64_t> thread_time_in_state_cache_;
+  uint32_t thread_time_in_state_cache_size_;
+
+  std::unique_ptr<CpuFreqInfo> cpu_freq_info_;
+
   // If true, the next trace packet will have the |incremental_state_cleared|
   // flag set. Set when handling a ClearIncrementalState call.
   //
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 e3e4b9d..f9a2697 100644
--- a/src/traced/probes/ps/process_stats_data_source_unittest.cc
+++ b/src/traced/probes/ps/process_stats_data_source_unittest.cc
@@ -22,6 +22,7 @@
 #include "perfetto/protozero/scattered_heap_buffer.h"
 #include "perfetto/tracing/core/data_source_config.h"
 #include "src/base/test/test_task_runner.h"
+#include "src/traced/probes/common/cpu_freq_info_for_testing.h"
 #include "src/tracing/core/trace_writer_for_testing.h"
 #include "test/gtest_and_gmock.h"
 
@@ -31,6 +32,7 @@
 
 using ::perfetto::protos::gen::ProcessStatsConfig;
 using ::testing::_;
+using ::testing::ElementsAre;
 using ::testing::ElementsAreArray;
 using ::testing::Invoke;
 using ::testing::Mock;
@@ -45,11 +47,17 @@
   TestProcessStatsDataSource(base::TaskRunner* task_runner,
                              TracingSessionID id,
                              std::unique_ptr<TraceWriter> writer,
-                             const DataSourceConfig& config)
-      : ProcessStatsDataSource(task_runner, id, std::move(writer), config) {}
+                             const DataSourceConfig& config,
+                             std::unique_ptr<CpuFreqInfo> cpu_freq_info)
+      : ProcessStatsDataSource(task_runner,
+                               id,
+                               std::move(writer),
+                               config,
+                               std::move(cpu_freq_info)) {}
 
   MOCK_METHOD0(OpenProcDir, base::ScopedDir());
   MOCK_METHOD2(ReadProcPidFile, std::string(int32_t pid, const std::string&));
+  MOCK_METHOD1(OpenProcTaskDir, base::ScopedDir(int32_t pid));
 };
 
 class ProcessStatsDataSourceTest : public ::testing::Test {
@@ -62,12 +70,14 @@
         std::unique_ptr<TraceWriterForTesting>(new TraceWriterForTesting());
     writer_raw_ = writer.get();
     return std::unique_ptr<TestProcessStatsDataSource>(
-        new TestProcessStatsDataSource(&task_runner_, 0, std::move(writer),
-                                       cfg));
+        new TestProcessStatsDataSource(
+            &task_runner_, 0, std::move(writer), cfg,
+            cpu_freq_info_for_testing.GetInstance()));
   }
 
   base::TestTaskRunner task_runner_;
   TraceWriterForTesting* writer_raw_;
+  CpuFreqInfoForTesting cpu_freq_info_for_testing;
 };
 
 TEST_F(ProcessStatsDataSourceTest, WriteOnceProcess) {
@@ -454,5 +464,117 @@
   rmdir(path);
 }
 
+TEST_F(ProcessStatsDataSourceTest, ThreadTimeInState) {
+  DataSourceConfig ds_config;
+  ProcessStatsConfig config;
+  // Do 2 ticks before cache clear.
+  config.set_proc_stats_poll_ms(100);
+  config.set_proc_stats_cache_ttl_ms(200);
+  config.add_quirks(ProcessStatsConfig::DISABLE_ON_DEMAND);
+  config.set_record_thread_time_in_state(true);
+  ds_config.set_process_stats_config_raw(config.SerializeAsString());
+  auto data_source = GetProcessStatsDataSource(ds_config);
+
+  std::vector<std::string> dirs_to_delete;
+  auto make_proc_path = [&dirs_to_delete](base::TempDir& temp_dir, int pid) {
+    char path[256];
+    sprintf(path, "%s/%d", temp_dir.path().c_str(), pid);
+    dirs_to_delete.push_back(path);
+    mkdir(path, 0755);
+  };
+  // Populate a fake /proc/ directory.
+  auto fake_proc = base::TempDir::Create();
+  const int kPid = 1;
+  make_proc_path(fake_proc, kPid);
+
+  // Populate a fake /proc/1/task directory.
+  auto fake_proc_task = base::TempDir::Create();
+  const int kTids[] = {1, 2};
+  for (int tid : kTids)
+    make_proc_path(fake_proc_task, tid);
+
+  auto checkpoint = task_runner_.CreateCheckpoint("all_done");
+
+  EXPECT_CALL(*data_source, OpenProcDir()).WillRepeatedly(Invoke([&fake_proc] {
+    return base::ScopedDir(opendir(fake_proc.path().c_str()));
+  }));
+  EXPECT_CALL(*data_source, ReadProcPidFile(kPid, "status"))
+      .WillRepeatedly(
+          Return("Name:	pid_10\nVmSize:	 100 kB\nVmRSS:\t100  kB\n"));
+  EXPECT_CALL(*data_source, ReadProcPidFile(kPid, "oom_score_adj"))
+      .WillRepeatedly(Return("900"));
+  EXPECT_CALL(*data_source, OpenProcTaskDir(kPid))
+      .WillRepeatedly(Invoke([&fake_proc_task](int32_t) {
+        return base::ScopedDir(opendir(fake_proc_task.path().c_str()));
+      }));
+  EXPECT_CALL(*data_source, ReadProcPidFile(kTids[0], "time_in_state"))
+      .Times(3)
+      .WillRepeatedly(Return("cpu0\n300000 1\n748800 1\ncpu1\n300000 5\n"));
+  EXPECT_CALL(*data_source, ReadProcPidFile(kTids[1], "time_in_state"))
+      .WillOnce(
+          Return("cpu0\n300000 10\n748800 0\ncpu1\n300000 50\n652800 60\n"))
+      .WillOnce(Return("cpu0\n300000 20\n748800 0\n1324800 30\ncpu1\n300000 "
+                       "100\n652800 60\n"))
+      .WillOnce(Invoke([&checkpoint](int32_t, const std::string&) {
+        // Call checkpoint here to stop after the third tick.
+        checkpoint();
+        return "cpu0\n300000 200\n748800 0\n1324800 30\ncpu1\n300000 "
+               "100\n652800 60\n";
+      }));
+
+  data_source->Start();
+  task_runner_.RunUntilCheckpoint("all_done");
+  data_source->Flush(1 /* FlushRequestId */, []() {});
+
+  std::vector<protos::gen::ProcessStats::Process> processes;
+  for (const auto& packet : writer_raw_->GetAllTracePackets())
+    for (const auto& process : packet.process_stats().processes())
+      processes.push_back(process);
+
+  EXPECT_EQ(processes.size(), 3u);
+
+  auto compare_tid = [](protos::gen::ProcessStats_Thread& l,
+                        protos::gen::ProcessStats_Thread& r) {
+    return l.tid() < r.tid();
+  };
+
+  // First pull has all threads.
+  auto threads = processes[0].threads();
+  EXPECT_EQ(threads.size(), 2u);
+  std::sort(threads.begin(), threads.end(), compare_tid);
+  auto thread = threads[0];
+  EXPECT_EQ(thread.tid(), 1);
+  EXPECT_THAT(thread.cpu_freq_indices(), ElementsAre(1u, 3u, 10u));
+  EXPECT_THAT(thread.cpu_freq_ticks(), ElementsAre(1, 1, 5));
+  thread = threads[1];
+  EXPECT_EQ(thread.tid(), 2);
+  EXPECT_THAT(thread.cpu_freq_indices(), ElementsAre(1u, 10u, 11u));
+  EXPECT_THAT(thread.cpu_freq_ticks(), ElementsAre(10, 50, 60));
+
+  // Second pull has only one thread with delta.
+  threads = processes[1].threads();
+  EXPECT_EQ(threads.size(), 1u);
+  thread = threads[0];
+  EXPECT_EQ(thread.tid(), 2);
+  EXPECT_THAT(thread.cpu_freq_indices(), ElementsAre(1u, 6u, 10u));
+  EXPECT_THAT(thread.cpu_freq_ticks(), ElementsAre(20, 30, 100));
+
+  // Third pull has all thread because cache was cleared.
+  threads = processes[2].threads();
+  EXPECT_EQ(threads.size(), 2u);
+  std::sort(threads.begin(), threads.end(), compare_tid);
+  thread = threads[0];
+  EXPECT_EQ(thread.tid(), 1);
+  EXPECT_THAT(thread.cpu_freq_indices(), ElementsAre(1u, 3u, 10u));
+  EXPECT_THAT(thread.cpu_freq_ticks(), ElementsAre(1, 1, 5));
+  thread = threads[1];
+  EXPECT_EQ(thread.tid(), 2);
+  EXPECT_THAT(thread.cpu_freq_indices(), ElementsAre(1u, 6u, 10u, 11u));
+  EXPECT_THAT(thread.cpu_freq_ticks(), ElementsAre(200, 30, 100, 60));
+
+  for (const std::string& path : dirs_to_delete)
+    rmdir(path.c_str());
+}
+
 }  // namespace
 }  // namespace perfetto
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/probes/sys_stats/sys_stats_data_source.cc b/src/traced/probes/sys_stats/sys_stats_data_source.cc
index 132e279..2d939ec 100644
--- a/src/traced/probes/sys_stats/sys_stats_data_source.cc
+++ b/src/traced/probes/sys_stats/sys_stats_data_source.cc
@@ -66,14 +66,17 @@
 }  // namespace
 
 // static
-constexpr int SysStatsDataSource::kTypeId;
+const ProbesDataSource::Descriptor SysStatsDataSource::descriptor = {
+    /*name*/ "linux.sys_stats",
+    /*flags*/ Descriptor::kFlagsNone,
+};
 
 SysStatsDataSource::SysStatsDataSource(base::TaskRunner* task_runner,
                                        TracingSessionID session_id,
                                        std::unique_ptr<TraceWriter> writer,
                                        const DataSourceConfig& ds_config,
                                        OpenFunction open_fn)
-    : ProbesDataSource(session_id, kTypeId),
+    : ProbesDataSource(session_id, &descriptor),
       task_runner_(task_runner),
       writer_(std::move(writer)),
       weak_factory_(this) {
diff --git a/src/traced/probes/sys_stats/sys_stats_data_source.h b/src/traced/probes/sys_stats/sys_stats_data_source.h
index ad33071..df47176 100644
--- a/src/traced/probes/sys_stats/sys_stats_data_source.h
+++ b/src/traced/probes/sys_stats/sys_stats_data_source.h
@@ -45,7 +45,8 @@
 
 class SysStatsDataSource : public ProbesDataSource {
  public:
-  static constexpr int kTypeId = 4;
+  static const ProbesDataSource::Descriptor descriptor;
+
   using OpenFunction = base::ScopedFile (*)(const char*);
   SysStatsDataSource(base::TaskRunner*,
                      TracingSessionID,
diff --git a/src/traced/probes/system_info/BUILD.gn b/src/traced/probes/system_info/BUILD.gn
new file mode 100644
index 0000000..1889445
--- /dev/null
+++ b/src/traced/probes/system_info/BUILD.gn
@@ -0,0 +1,45 @@
+# 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/test.gni")
+
+source_set("system_info") {
+  public_deps = [ "../../../tracing/core" ]
+  deps = [
+    "..:data_source",
+    "../../../../gn:default_deps",
+    "../../../../include/perfetto/ext/traced",
+    "../../../../protos/perfetto/trace:zero",
+    "../../../../protos/perfetto/trace/system_info:zero",
+    "../../../base",
+    "../common",
+  ]
+  sources = [
+    "system_info_data_source.cc",
+    "system_info_data_source.h",
+  ]
+}
+
+perfetto_unittest_source_set("unittests") {
+  testonly = true
+  deps = [
+    ":system_info",
+    "../../../../gn:default_deps",
+    "../../../../gn:gtest_and_gmock",
+    "../../../../protos/perfetto/trace/system_info:cpp",
+    "../../../../src/tracing/test:test_support",
+    "../common:test_support",
+  ]
+  sources = [ "system_info_data_source_unittest.cc" ]
+}
diff --git a/src/traced/probes/system_info/system_info_data_source.cc b/src/traced/probes/system_info/system_info_data_source.cc
new file mode 100644
index 0000000..3e96239
--- /dev/null
+++ b/src/traced/probes/system_info/system_info_data_source.cc
@@ -0,0 +1,115 @@
+/*
+ * 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/traced/probes/system_info/system_info_data_source.h"
+
+#include "perfetto/base/time.h"
+#include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/ext/base/string_utils.h"
+
+#include "protos/perfetto/trace/system_info/cpu_info.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto {
+
+namespace {
+
+// Key for default processor string in /proc/cpuinfo as seen on arm. Note the
+// uppercase P.
+const char kDefaultProcessor[] = "Processor";
+
+// Key for processor entry in /proc/cpuinfo. Used to determine whether a group
+// of lines describes a CPU.
+const char kProcessor[] = "processor";
+
+}  // namespace
+
+// static
+const ProbesDataSource::Descriptor SystemInfoDataSource::descriptor = {
+    /* name */ "linux.system_info",
+    /* flags */ Descriptor::kFlagsNone,
+};
+
+SystemInfoDataSource::SystemInfoDataSource(
+    TracingSessionID session_id,
+    std::unique_ptr<TraceWriter> writer,
+    std::unique_ptr<CpuFreqInfo> cpu_freq_info)
+    : ProbesDataSource(session_id, &descriptor),
+      writer_(std::move(writer)),
+      cpu_freq_info_(std::move(cpu_freq_info)) {}
+
+void SystemInfoDataSource::Start() {
+  auto packet = writer_->NewTracePacket();
+  packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
+  auto* cpu_info = packet->set_cpu_info();
+
+  // Parse /proc/cpuinfo which contains groups of "key\t: value" lines separated
+  // by an empty line. Each group represents a CPU. See the full example in the
+  // unittest.
+  std::string proc_cpu_info = ReadFile("/proc/cpuinfo");
+  std::string::iterator line_start = proc_cpu_info.begin();
+  std::string::iterator line_end = proc_cpu_info.end();
+  std::string default_processor = "unknown";
+  std::string cpu_index = "";
+  uint32_t next_cpu_index = 0;
+  while (line_start != proc_cpu_info.end()) {
+    line_end = find(line_start, proc_cpu_info.end(), '\n');
+    if (line_end == proc_cpu_info.end())
+      break;
+    std::string line = std::string(line_start, line_end);
+    line_start = line_end + 1;
+    if (line.empty() && !cpu_index.empty()) {
+      PERFETTO_DCHECK(cpu_index == std::to_string(next_cpu_index));
+      auto* cpu = cpu_info->add_cpus();
+      cpu->set_processor(default_processor);
+      auto freqs_range = cpu_freq_info_->GetFreqs(next_cpu_index);
+      for (auto it = freqs_range.first; it != freqs_range.second; it++) {
+        cpu->add_frequencies(*it);
+      }
+      cpu_index = "";
+      next_cpu_index++;
+      continue;
+    }
+    auto splits = base::SplitString(line, ":");
+    if (splits.size() != 2)
+      continue;
+    std::string key =
+        base::StripSuffix(base::StripChars(splits[0], "\t", ' '), " ");
+    std::string value = base::StripPrefix(splits[1], " ");
+    if (key == kDefaultProcessor)
+      default_processor = value;
+    else if (key == kProcessor)
+      cpu_index = value;
+  }
+
+  packet->Finalize();
+  writer_->Flush();
+}
+
+void SystemInfoDataSource::Flush(FlushRequestID,
+                                 std::function<void()> callback) {
+  writer_->Flush(callback);
+}
+
+std::string SystemInfoDataSource::ReadFile(std::string path) {
+  std::string contents;
+  if (!base::ReadFile(path, &contents))
+    return "";
+  return contents;
+}
+
+}  // namespace perfetto
diff --git a/src/traced/probes/system_info/system_info_data_source.h b/src/traced/probes/system_info/system_info_data_source.h
new file mode 100644
index 0000000..ceb8293
--- /dev/null
+++ b/src/traced/probes/system_info/system_info_data_source.h
@@ -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.
+ */
+
+#ifndef SRC_TRACED_PROBES_SYSTEM_INFO_SYSTEM_INFO_DATA_SOURCE_H_
+#define SRC_TRACED_PROBES_SYSTEM_INFO_SYSTEM_INFO_DATA_SOURCE_H_
+
+#include <memory>
+
+#include "perfetto/ext/tracing/core/trace_writer.h"
+#include "src/traced/probes/common/cpu_freq_info.h"
+#include "src/traced/probes/probes_data_source.h"
+
+namespace perfetto {
+
+class SystemInfoDataSource : public ProbesDataSource {
+ public:
+  static const ProbesDataSource::Descriptor descriptor;
+
+  SystemInfoDataSource(TracingSessionID,
+                       std::unique_ptr<TraceWriter> writer,
+                       std::unique_ptr<CpuFreqInfo> cpu_freq_info);
+
+  // ProbesDataSource implementation.
+  void Start() override;
+  void Flush(FlushRequestID, std::function<void()> callback) override;
+
+  // Virtual for testing.
+  virtual std::string ReadFile(std::string path);
+
+ private:
+  std::unique_ptr<TraceWriter> writer_;
+  std::unique_ptr<CpuFreqInfo> cpu_freq_info_;
+};
+
+}  // namespace perfetto
+
+#endif  // SRC_TRACED_PROBES_SYSTEM_INFO_SYSTEM_INFO_DATA_SOURCE_H_
diff --git a/src/traced/probes/system_info/system_info_data_source_unittest.cc b/src/traced/probes/system_info/system_info_data_source_unittest.cc
new file mode 100644
index 0000000..e706831
--- /dev/null
+++ b/src/traced/probes/system_info/system_info_data_source_unittest.cc
@@ -0,0 +1,160 @@
+/*
+ * 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/traced/probes/system_info/system_info_data_source.h"
+#include "src/traced/probes/common/cpu_freq_info_for_testing.h"
+#include "src/tracing/core/trace_writer_for_testing.h"
+#include "test/gtest_and_gmock.h"
+
+#include "protos/perfetto/trace/system_info/cpu_info.gen.h"
+
+using ::testing::AnyOf;
+using ::testing::ElementsAre;
+using ::testing::Return;
+
+namespace perfetto {
+namespace {
+
+const char kMockCpuInfoAndroid[] = R"(
+Processor	: AArch64 Processor rev 13 (aarch64)
+processor	: 0
+BogoMIPS	: 38.00
+Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp
+CPU implementer	: 0x51
+CPU architecture: 8
+CPU variant	: 0x7
+CPU part	: 0x803
+CPU revision	: 12
+
+processor	: 1
+BogoMIPS	: 38.00
+Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp
+CPU implementer	: 0x51
+CPU architecture: 8
+CPU variant	: 0x7
+CPU part	: 0x803
+CPU revision	: 12
+
+processor	: 2
+BogoMIPS	: 38.00
+Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp
+CPU implementer	: 0x51
+CPU architecture: 8
+CPU variant	: 0x7
+CPU part	: 0x803
+CPU revision	: 12
+
+processor	: 3
+BogoMIPS	: 38.00
+Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp
+CPU implementer	: 0x51
+CPU architecture: 8
+CPU variant	: 0x7
+CPU part	: 0x803
+CPU revision	: 12
+
+processor	: 4
+BogoMIPS	: 38.00
+Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp
+CPU implementer	: 0x51
+CPU architecture: 8
+CPU variant	: 0x7
+CPU part	: 0x803
+CPU revision	: 12
+
+processor	: 5
+BogoMIPS	: 38.00
+Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp
+CPU implementer	: 0x51
+CPU architecture: 8
+CPU variant	: 0x7
+CPU part	: 0x803
+CPU revision	: 12
+
+processor	: 6
+BogoMIPS	: 38.00
+Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp
+CPU implementer	: 0x51
+CPU architecture: 8
+CPU variant	: 0x6
+CPU part	: 0x802
+CPU revision	: 13
+
+processor	: 7
+BogoMIPS	: 38.00
+Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp
+CPU implementer	: 0x51
+CPU architecture: 8
+CPU variant	: 0x6
+CPU part	: 0x802
+CPU revision	: 13
+
+Hardware	: Qualcomm Technologies, Inc SDM670
+
+)";
+
+class TestSystemInfoDataSource : public SystemInfoDataSource {
+ public:
+  TestSystemInfoDataSource(std::unique_ptr<TraceWriter> writer,
+                           std::unique_ptr<CpuFreqInfo> cpu_freq_info)
+      : SystemInfoDataSource(
+            /* session_id */ 0,
+            std::move(writer),
+            std::move(cpu_freq_info)) {}
+
+  MOCK_METHOD1(ReadFile, std::string(std::string));
+};
+
+class SystemInfoDataSourceTest : public ::testing::Test {
+ protected:
+  std::unique_ptr<TestSystemInfoDataSource> GetSystemInfoDataSource() {
+    auto writer =
+        std::unique_ptr<TraceWriterForTesting>(new TraceWriterForTesting());
+    writer_raw_ = writer.get();
+    auto instance =
+        std::unique_ptr<TestSystemInfoDataSource>(new TestSystemInfoDataSource(
+            std::move(writer), cpu_freq_info_for_testing.GetInstance()));
+    return instance;
+  }
+
+  TraceWriterForTesting* writer_raw_ = nullptr;
+  CpuFreqInfoForTesting cpu_freq_info_for_testing;
+};
+
+TEST_F(SystemInfoDataSourceTest, CpuInfoAndroid) {
+  auto data_source = GetSystemInfoDataSource();
+  EXPECT_CALL(*data_source, ReadFile("/proc/cpuinfo"))
+      .WillOnce(Return(kMockCpuInfoAndroid));
+  data_source->Start();
+
+  protos::gen::TracePacket packet = writer_raw_->GetOnlyTracePacket();
+  ASSERT_TRUE(packet.has_cpu_info());
+  auto cpu_info = packet.cpu_info();
+  ASSERT_EQ(cpu_info.cpus_size(), 8);
+  auto cpu = cpu_info.cpus()[0];
+  ASSERT_EQ(cpu.processor(), "AArch64 Processor rev 13 (aarch64)");
+  ASSERT_THAT(cpu.frequencies(),
+              ElementsAre(300000, 576000, 748800, 998400, 1209600, 1324800,
+                          1516800, 1612800, 1708800));
+  cpu = cpu_info.cpus()[1];
+  ASSERT_EQ(cpu.processor(), "AArch64 Processor rev 13 (aarch64)");
+  ASSERT_THAT(cpu.frequencies(),
+              ElementsAre(300000, 652800, 825600, 979200, 1132800, 1363200,
+                          1536000, 1747200, 1843200, 1996800));
+}
+
+}  // namespace
+}  // namespace perfetto
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/traced/service/builtin_producer.cc b/src/traced/service/builtin_producer.cc
index 16df2e1..8538c00 100644
--- a/src/traced/service/builtin_producer.cc
+++ b/src/traced/service/builtin_producer.cc
@@ -40,7 +40,9 @@
 
 constexpr char kHeapprofdDataSourceName[] = "android.heapprofd";
 constexpr char kJavaHprofDataSourceName[] = "android.java_hprof";
+constexpr char kTracedPerfDataSourceName[] = "linux.perf";
 constexpr char kLazyHeapprofdPropertyName[] = "traced.lazy.heapprofd";
+constexpr char kLazyTracedPerfPropertyName[] = "traced.lazy.traced_perf";
 
 }  // namespace
 
@@ -48,16 +50,22 @@
                                  uint32_t lazy_stop_delay_ms)
     : task_runner_(task_runner), weak_factory_(this) {
   lazy_heapprofd_.stop_delay_ms = lazy_stop_delay_ms;
+  lazy_traced_perf_.stop_delay_ms = lazy_stop_delay_ms;
 }
 
 BuiltinProducer::~BuiltinProducer() {
   if (!lazy_heapprofd_.instance_ids.empty())
-    SetAndroidProperty(kLazyHeapprofdPropertyName, "0");
+    SetAndroidProperty(kLazyHeapprofdPropertyName, "");
+  if (!lazy_traced_perf_.instance_ids.empty())
+    SetAndroidProperty(kLazyTracedPerfPropertyName, "");
 }
 
 void BuiltinProducer::ConnectInProcess(TracingService* svc) {
-  endpoint_ = svc->ConnectProducer(this, geteuid(), "traced",
-                                   /*shm_hint_kb*/ 16, /*in_process*/ true);
+  endpoint_ = svc->ConnectProducer(
+      this, geteuid(), "traced",
+      /*shared_memory_size_hint_bytes=*/16 * 1024, /*in_process=*/true,
+      TracingService::ProducerSMBScrapingMode::kDisabled,
+      /*shmem_page_size_hint_bytes=*/4096);
 }
 
 void BuiltinProducer::OnConnect() {
@@ -65,18 +73,21 @@
   metatrace_dsd.set_name(MetatraceWriter::kDataSourceName);
   metatrace_dsd.set_will_notify_on_stop(true);
   endpoint_->RegisterDataSource(metatrace_dsd);
-
   {
     DataSourceDescriptor lazy_heapprofd_dsd;
     lazy_heapprofd_dsd.set_name(kHeapprofdDataSourceName);
     endpoint_->RegisterDataSource(lazy_heapprofd_dsd);
   }
-
   {
     DataSourceDescriptor lazy_java_hprof_dsd;
     lazy_java_hprof_dsd.set_name(kJavaHprofDataSourceName);
     endpoint_->RegisterDataSource(lazy_java_hprof_dsd);
   }
+  {
+    DataSourceDescriptor lazy_traced_perf_dsd;
+    lazy_traced_perf_dsd.set_name(kTracedPerfDataSourceName);
+    endpoint_->RegisterDataSource(lazy_traced_perf_dsd);
+  }
 }
 
 void BuiltinProducer::SetupDataSource(DataSourceInstanceID ds_id,
@@ -86,6 +97,14 @@
     SetAndroidProperty(kLazyHeapprofdPropertyName, "1");
     lazy_heapprofd_.generation++;
     lazy_heapprofd_.instance_ids.emplace(ds_id);
+    return;
+  }
+
+  if (ds_config.name() == kTracedPerfDataSourceName) {
+    SetAndroidProperty(kLazyTracedPerfPropertyName, "1");
+    lazy_traced_perf_.generation++;
+    lazy_traced_perf_.instance_ids.emplace(ds_id);
+    return;
   }
 }
 
@@ -114,24 +133,33 @@
     meta_it->second.WriteAllAndFlushTraceWriter([] {});
     metatrace_.writers.erase(meta_it);
     endpoint_->NotifyDataSourceStopped(ds_id);
+    return;
   }
 
-  auto lazy_it = lazy_heapprofd_.instance_ids.find(ds_id);
-  if (lazy_it != lazy_heapprofd_.instance_ids.end()) {
-    lazy_heapprofd_.instance_ids.erase(lazy_it);
+  MaybeInitiateLazyStop(ds_id, &lazy_heapprofd_, kLazyHeapprofdPropertyName);
+  MaybeInitiateLazyStop(ds_id, &lazy_traced_perf_, kLazyTracedPerfPropertyName);
+}
 
-    // if no more sessions - stop heapprofd after a delay
-    if (lazy_heapprofd_.instance_ids.empty()) {
-      uint64_t cur_generation = lazy_heapprofd_.generation;
+void BuiltinProducer::MaybeInitiateLazyStop(DataSourceInstanceID ds_id,
+                                            LazyAndroidDaemonState* lazy_state,
+                                            const char* prop_name) {
+  auto lazy_it = lazy_state->instance_ids.find(ds_id);
+  if (lazy_it != lazy_state->instance_ids.end()) {
+    lazy_state->instance_ids.erase(lazy_it);
+
+    // if no more sessions - stop daemon after a delay
+    if (lazy_state->instance_ids.empty()) {
+      uint64_t cur_generation = lazy_state->generation;
       auto weak_this = weak_factory_.GetWeakPtr();
       task_runner_->PostDelayedTask(
-          [weak_this, cur_generation] {
+          [weak_this, cur_generation, lazy_state, prop_name] {
             if (!weak_this)
               return;
-            if (weak_this->lazy_heapprofd_.generation == cur_generation)
-              weak_this->SetAndroidProperty(kLazyHeapprofdPropertyName, "0");
+            // |lazy_state| should be valid if the |weak_this| is still valid
+            if (lazy_state->generation == cur_generation)
+              weak_this->SetAndroidProperty(prop_name, "");
           },
-          lazy_heapprofd_.stop_delay_ms);
+          lazy_state->stop_delay_ms);
     }
   }
 }
@@ -144,7 +172,7 @@
     if (meta_it != metatrace_.writers.end()) {
       meta_it->second.WriteAllAndFlushTraceWriter([] {});
     }
-    // nothing to be done for lazy heapprofd sources
+    // nothing to be done for lazy sources
   }
   endpoint_->NotifyFlushComplete(flush_id);
 }
diff --git a/src/traced/service/builtin_producer.h b/src/traced/service/builtin_producer.h
index 065310b..9873458 100644
--- a/src/traced/service/builtin_producer.h
+++ b/src/traced/service/builtin_producer.h
@@ -33,6 +33,7 @@
 // Data sources built into the tracing service daemon (traced):
 // * perfetto metatrace
 // * lazy heapprofd daemon starter (android only)
+// * lazy traced_perf daemon starter (android only)
 class BuiltinProducer : public Producer {
  public:
   BuiltinProducer(base::TaskRunner* task_runner, uint32_t lazy_stop_delay_ms);
@@ -63,20 +64,25 @@
     std::map<DataSourceInstanceID, MetatraceWriter> writers;
   };
 
-  struct LazyHeapprofdState {
+  struct LazyAndroidDaemonState {
     // Track active instances to know when to stop.
     std::set<DataSourceInstanceID> instance_ids;
-    // Delay between the last heapprofd session stopping, and the lazy system
-    // property being unset (to shut down heapprofd).
+    // Delay between the last matching session stopping, and the lazy system
+    // property being unset (to shut down the daemon).
     uint32_t stop_delay_ms;
     uint64_t generation = 0;
   };
 
+  void MaybeInitiateLazyStop(DataSourceInstanceID ds_id,
+                             LazyAndroidDaemonState* lazy_state,
+                             const char* prop_name);
+
   base::TaskRunner* const task_runner_;
   std::unique_ptr<TracingService::ProducerEndpoint> endpoint_;
 
   MetatraceState metatrace_;
-  LazyHeapprofdState lazy_heapprofd_;
+  LazyAndroidDaemonState lazy_heapprofd_;
+  LazyAndroidDaemonState lazy_traced_perf_;
 
   base::WeakPtrFactory<BuiltinProducer> weak_factory_;  // Keep last.
 };
diff --git a/src/traced/service/builtin_producer_unittest.cc b/src/traced/service/builtin_producer_unittest.cc
index 80b17fe..f95f9ec 100644
--- a/src/traced/service/builtin_producer_unittest.cc
+++ b/src/traced/service/builtin_producer_unittest.cc
@@ -23,13 +23,16 @@
 namespace perfetto {
 namespace {
 
-constexpr const char kHeapprofdDataSourceName[] = "android.heapprofd";
-constexpr const char kLazyHeapprofdPropertyName[] = "traced.lazy.heapprofd";
+constexpr char kHeapprofdDataSourceName[] = "android.heapprofd";
+constexpr char kTracedPerfDataSourceName[] = "linux.perf";
+constexpr char kLazyHeapprofdPropertyName[] = "traced.lazy.heapprofd";
+constexpr char kLazyTracedPerfPropertyName[] = "traced.lazy.traced_perf";
 
 using ::testing::_;
-using ::testing::InSequence;
 using ::testing::InvokeWithoutArgs;
+using ::testing::Mock;
 using ::testing::Return;
+using ::testing::StrictMock;
 
 class MockBuiltinProducer : public BuiltinProducer {
  public:
@@ -45,11 +48,30 @@
   cfg.set_name(kHeapprofdDataSourceName);
   base::TestTaskRunner task_runner;
   auto done = task_runner.CreateCheckpoint("done");
-  MockBuiltinProducer p(&task_runner);
-  InSequence s;
+  StrictMock<MockBuiltinProducer> p(&task_runner);
+  testing::InSequence s;
   EXPECT_CALL(p, SetAndroidProperty(kLazyHeapprofdPropertyName, "1"))
       .WillOnce(Return(true));
-  EXPECT_CALL(p, SetAndroidProperty(kLazyHeapprofdPropertyName, "0"))
+  EXPECT_CALL(p, SetAndroidProperty(kLazyHeapprofdPropertyName, ""))
+      .WillOnce(InvokeWithoutArgs([&done]() {
+        done();
+        return true;
+      }));
+  p.SetupDataSource(1, cfg);
+  p.StopDataSource(1);
+  task_runner.RunUntilCheckpoint("done");
+}
+
+TEST(BuiltinProducerTest, LazyTracedPerfSimple) {
+  DataSourceConfig cfg;
+  cfg.set_name(kTracedPerfDataSourceName);
+  base::TestTaskRunner task_runner;
+  auto done = task_runner.CreateCheckpoint("done");
+  StrictMock<MockBuiltinProducer> p(&task_runner);
+  testing::InSequence s;
+  EXPECT_CALL(p, SetAndroidProperty(kLazyTracedPerfPropertyName, "1"))
+      .WillOnce(Return(true));
+  EXPECT_CALL(p, SetAndroidProperty(kLazyTracedPerfPropertyName, ""))
       .WillOnce(InvokeWithoutArgs([&done]() {
         done();
         return true;
@@ -64,15 +86,15 @@
   cfg.set_name(kHeapprofdDataSourceName);
   base::TestTaskRunner task_runner;
   auto done = task_runner.CreateCheckpoint("done");
-  MockBuiltinProducer p(&task_runner);
-  InSequence s;
+  StrictMock<MockBuiltinProducer> p(&task_runner);
+  testing::InSequence s;
   EXPECT_CALL(p, SetAndroidProperty(kLazyHeapprofdPropertyName, "1"))
       .WillRepeatedly(Return(true));
   p.SetupDataSource(1, cfg);
   p.SetupDataSource(2, cfg);
   p.StopDataSource(2);
   task_runner.RunUntilIdle();
-  EXPECT_CALL(p, SetAndroidProperty(kLazyHeapprofdPropertyName, "0"))
+  EXPECT_CALL(p, SetAndroidProperty(kLazyHeapprofdPropertyName, ""))
       .WillOnce(InvokeWithoutArgs([&done]() {
         done();
         return true;
@@ -86,8 +108,8 @@
   cfg.set_name(kHeapprofdDataSourceName);
   base::TestTaskRunner task_runner;
   auto done = task_runner.CreateCheckpoint("done");
-  MockBuiltinProducer p(&task_runner);
-  InSequence s;
+  StrictMock<MockBuiltinProducer> p(&task_runner);
+  testing::InSequence s;
   EXPECT_CALL(p, SetAndroidProperty(kLazyHeapprofdPropertyName, "1"))
       .WillRepeatedly(Return(true));
   p.SetupDataSource(1, cfg);
@@ -95,7 +117,7 @@
   p.SetupDataSource(2, cfg);
   task_runner.RunUntilIdle();
   p.StopDataSource(2);
-  EXPECT_CALL(p, SetAndroidProperty(kLazyHeapprofdPropertyName, "0"))
+  EXPECT_CALL(p, SetAndroidProperty(kLazyHeapprofdPropertyName, ""))
       .WillOnce(InvokeWithoutArgs([&done]() {
         done();
         return true;
@@ -103,5 +125,40 @@
   task_runner.RunUntilCheckpoint("done");
 }
 
+TEST(BuiltinProducerTest, LazyRefCountsIndependent) {
+  DataSourceConfig cfg_perf;
+  cfg_perf.set_name(kTracedPerfDataSourceName);
+  DataSourceConfig cfg_heap;
+  cfg_heap.set_name(kHeapprofdDataSourceName);
+
+  base::TestTaskRunner task_runner;
+  StrictMock<MockBuiltinProducer> p(&task_runner);
+  testing::InSequence s;
+
+  // start one instance of both types of sources
+  EXPECT_CALL(p, SetAndroidProperty(kLazyHeapprofdPropertyName, "1"))
+      .WillOnce(Return(true));
+  EXPECT_CALL(p, SetAndroidProperty(kLazyTracedPerfPropertyName, "1"))
+      .WillOnce(Return(true));
+  p.SetupDataSource(1, cfg_heap);
+  p.SetupDataSource(2, cfg_perf);
+  task_runner.RunUntilIdle();
+  Mock::VerifyAndClearExpectations(&p);
+
+  // stop heapprofd source
+  EXPECT_CALL(p, SetAndroidProperty(kLazyHeapprofdPropertyName, ""))
+      .WillOnce(Return(true));
+  p.StopDataSource(1);
+  task_runner.RunUntilIdle();
+  Mock::VerifyAndClearExpectations(&p);
+
+  // stop traced_perf source
+  EXPECT_CALL(p, SetAndroidProperty(kLazyTracedPerfPropertyName, ""))
+      .WillOnce(Return(true));
+  p.StopDataSource(2);
+  task_runner.RunUntilIdle();
+  Mock::VerifyAndClearExpectations(&p);
+}
+
 }  // namespace
 }  // namespace perfetto
diff --git a/src/tracing/BUILD.gn b/src/tracing/BUILD.gn
index 2ebb7da..a6ea494 100644
--- a/src/tracing/BUILD.gn
+++ b/src/tracing/BUILD.gn
@@ -17,180 +17,33 @@
 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).
+# The backends are designed to be dead-code-eliminated via linker's gc-section
+# when not use. See comments in Tracing::Initialize() in tracing.h.
+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_without_backends",
+    ":in_process_backend",
     "../../gn:default_deps",
     "../../include/perfetto/tracing",
-    "../../protos/perfetto/common:zero",
-    "../../protos/perfetto/config:zero",
-    "../../protos/perfetto/trace/perfetto:zero",  # For MetatraceWriter.
-    "../base",
-    "../protozero",
+    "../../include/perfetto/tracing/core",
   ]
-  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",
-  ]
-
   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",
-    ]
+    public_deps += [ ":system_backend" ]
   }
 }
 
-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
+# This target checks that the client API builds without backends. This is to
+# check that no references to the backends are leaked from the implementation
+# internals. In turn, this allows to dead-code-eliminate unused backends when
+# using linker's gc-sections (or similar mechanism).
+if (perfetto_build_standalone) {
+  shared_library("client_api_no_backends_compile_test") {
     deps = [
-      ":consumer_api_deprecated",
+      ":client_api_without_backends",
+      ":platform_fake",
       "../../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" ]
-  }
-}
-
-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") {
-    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",
-    ]
-    deps = [
-      ":tracing",
-      "../../gn:default_deps",
-      "../../protos/perfetto/ipc",
-      "../base",
-      "../ipc",
     ]
   }
 }
@@ -229,13 +82,15 @@
   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_without_backends") {
   deps = [
-    ":common",
     "../../include/perfetto/tracing/core",
+    "../../protos/perfetto/common:zero",
     "../../protos/perfetto/config:cpp",
+    "../../protos/perfetto/config/track_event:cpp",
     "../base",
-    "../tracing",
+    "core",
   ]
   public_deps = [
     "../../gn:default_deps",
@@ -245,8 +100,6 @@
     "data_source.cc",
     "debug_annotation.cc",
     "event_context.cc",
-    "internal/in_process_tracing_backend.cc",
-    "internal/in_process_tracing_backend.h",
     "internal/tracing_muxer_impl.cc",
     "internal/tracing_muxer_impl.h",
     "internal/track_event_internal.cc",
@@ -254,66 +107,60 @@
     "tracing.cc",
     "track.cc",
     "track_event_category_registry.cc",
+    "track_event_legacy.cc",
     "virtual_destructors.cc",
   ]
-
+  assert_no_deps = [ "core:service" ]
   if (enable_perfetto_ipc) {
-    deps += [ ":ipc" ]
-    sources += [
-      "internal/system_tracing_backend.cc",
-      "internal/system_tracing_backend.h",
+    assert_no_deps += [
+      "../ipc:common",
+      "ipc/common",
     ]
   }
 }
 
-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_backend") {
+    public_deps = [ "../../include/perfetto/tracing" ]
     deps = [
-      ":client_api",
-      ":platform_posix",
-      "../../:libperfetto_client_experimental",
+      ":client_api_without_backends",
       "../../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" ]
   }
 }
 
+# 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_without_backends",
+    "../../gn:default_deps",
+    "../../include/perfetto/tracing/core",
+    "../base",
+    "core:service",
+  ]
+  sources = [ "internal/in_process_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",
+      ":platform_posix",
+      "../..:libperfetto_client_experimental",
+      "../../../../../gn:benchmark",
+      "../../../../../gn:default_deps",
     ]
-    sources = [ "core/packet_stream_validator_benchmark.cc" ]
+    sources = [ "api_benchmark.cc" ]
   }
 }
-
-perfetto_fuzzer_test("packet_stream_validator_fuzzer") {
-  sources = [ "core/packet_stream_validator_fuzzer.cc" ]
-  deps = [
-    ":tracing",
-    "../../../../gn:default_deps",
-  ]
-}
diff --git a/src/tracing/api_benchmark.cc b/src/tracing/api_benchmark.cc
new file mode 100644
index 0000000..88d725e
--- /dev/null
+++ b/src/tracing/api_benchmark.cc
@@ -0,0 +1,132 @@
+// 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 <benchmark/benchmark.h>
+
+#include "perfetto/tracing.h"
+#include "protos/perfetto/trace/test_event.pbzero.h"
+#include "protos/perfetto/trace/track_event/log_message.pbzero.h"
+
+PERFETTO_DEFINE_CATEGORIES(perfetto::Category("benchmark"));
+PERFETTO_TRACK_EVENT_STATIC_STORAGE();
+
+namespace {
+
+class BenchmarkDataSource : public perfetto::DataSource<BenchmarkDataSource> {
+ public:
+  void OnSetup(const SetupArgs&) override {}
+  void OnStart(const StartArgs&) override {}
+  void OnStop(const StopArgs&) override {}
+};
+
+static void BM_TracingDataSourceDisabled(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    BenchmarkDataSource::Trace([&](BenchmarkDataSource::TraceContext) {});
+    benchmark::ClobberMemory();
+  }
+}
+
+std::unique_ptr<perfetto::TracingSession> StartTracing(
+    const std::string& data_source_name) {
+  perfetto::TracingInitArgs args;
+  args.backends = perfetto::kInProcessBackend;
+  perfetto::Tracing::Initialize(args);
+
+  perfetto::DataSourceDescriptor dsd;
+  dsd.set_name("benchmark");
+  BenchmarkDataSource::Register(dsd);
+  perfetto::TrackEvent::Register();
+
+  perfetto::TraceConfig cfg;
+  cfg.add_buffers()->set_size_kb(1024);
+  auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+  ds_cfg->set_name(data_source_name);
+  auto tracing_session =
+      perfetto::Tracing::NewTrace(perfetto::kInProcessBackend);
+  tracing_session->Setup(cfg);
+  tracing_session->StartBlocking();
+  return tracing_session;
+}
+
+static void BM_TracingDataSourceLambda(benchmark::State& state) {
+  auto tracing_session = StartTracing("benchmark");
+
+  while (state.KeepRunning()) {
+    BenchmarkDataSource::Trace([&](BenchmarkDataSource::TraceContext ctx) {
+      auto packet = ctx.NewTracePacket();
+      packet->set_timestamp(42);
+      packet->set_for_testing()->set_str("benchmark");
+    });
+    benchmark::ClobberMemory();
+  }
+
+  tracing_session->StopBlocking();
+  PERFETTO_CHECK(!tracing_session->ReadTraceBlocking().empty());
+}
+
+static void BM_TracingTrackEventDisabled(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    TRACE_EVENT_BEGIN("benchmark", "DisabledEvent");
+    benchmark::ClobberMemory();
+  }
+}
+
+static void BM_TracingTrackEventBasic(benchmark::State& state) {
+  auto tracing_session = StartTracing("track_event");
+
+  while (state.KeepRunning()) {
+    TRACE_EVENT_BEGIN("benchmark", "Event");
+    benchmark::ClobberMemory();
+  }
+
+  tracing_session->StopBlocking();
+  PERFETTO_CHECK(!tracing_session->ReadTraceBlocking().empty());
+}
+
+static void BM_TracingTrackEventDebugAnnotations(benchmark::State& state) {
+  auto tracing_session = StartTracing("track_event");
+
+  while (state.KeepRunning()) {
+    TRACE_EVENT_BEGIN("benchmark", "Event", "value", 42);
+    benchmark::ClobberMemory();
+  }
+
+  tracing_session->StopBlocking();
+  PERFETTO_CHECK(!tracing_session->ReadTraceBlocking().empty());
+}
+
+static void BM_TracingTrackEventLambda(benchmark::State& state) {
+  auto tracing_session = StartTracing("track_event");
+
+  while (state.KeepRunning()) {
+    TRACE_EVENT_BEGIN("benchmark", "Event", [&](perfetto::EventContext ctx) {
+      auto* log = ctx.event()->set_log_message();
+      log->set_source_location_iid(42);
+      log->set_body_iid(1234);
+    });
+    benchmark::ClobberMemory();
+  }
+
+  tracing_session->StopBlocking();
+  PERFETTO_CHECK(!tracing_session->ReadTraceBlocking().empty());
+}
+
+}  // namespace
+
+BENCHMARK(BM_TracingDataSourceDisabled);
+BENCHMARK(BM_TracingDataSourceLambda);
+BENCHMARK(BM_TracingTrackEventBasic);
+BENCHMARK(BM_TracingTrackEventDebugAnnotations);
+BENCHMARK(BM_TracingTrackEventDisabled);
+BENCHMARK(BM_TracingTrackEventLambda);
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..141d250
--- /dev/null
+++ b/src/tracing/core/BUILD.gn
@@ -0,0 +1,148 @@
+# 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",
+    "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",
+    "../../../protos/perfetto/trace/perfetto: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",
+      "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.cc b/src/tracing/core/shared_memory_arbiter_impl.cc
index 6141afb..c19c96a 100644
--- a/src/tracing/core/shared_memory_arbiter_impl.cc
+++ b/src/tracing/core/shared_memory_arbiter_impl.cc
@@ -16,27 +16,46 @@
 
 #include "src/tracing/core/shared_memory_arbiter_impl.h"
 
+#include <algorithm>
+#include <limits>
+#include <utility>
+
 #include "perfetto/base/logging.h"
 #include "perfetto/base/task_runner.h"
 #include "perfetto/base/time.h"
 #include "perfetto/ext/tracing/core/commit_data_request.h"
 #include "perfetto/ext/tracing/core/shared_memory.h"
-#include "perfetto/ext/tracing/core/startup_trace_writer_registry.h"
 #include "src/tracing/core/null_trace_writer.h"
 #include "src/tracing/core/trace_writer_impl.h"
 
-#include <limits>
-#include <utility>
-
 namespace perfetto {
 
 using Chunk = SharedMemoryABI::Chunk;
 
+namespace {
+static_assert(sizeof(BufferID) == sizeof(uint16_t),
+              "The MaybeUnboundBufferID logic requires BufferID not to grow "
+              "above uint16_t.");
+
+MaybeUnboundBufferID MakeTargetBufferIdForReservation(uint16_t reservation_id) {
+  // Reservation IDs are stored in the upper bits.
+  PERFETTO_CHECK(reservation_id > 0);
+  return static_cast<MaybeUnboundBufferID>(reservation_id) << 16;
+}
+
+bool IsReservationTargetBufferId(MaybeUnboundBufferID buffer_id) {
+  return (buffer_id >> 16) > 0;
+}
+}  // namespace
+
 // static
 SharedMemoryABI::PageLayout SharedMemoryArbiterImpl::default_page_layout =
     SharedMemoryABI::PageLayout::kPageDiv1;
 
 // static
+constexpr BufferID SharedMemoryArbiterImpl::kInvalidBufferId;
+
+// static
 std::unique_ptr<SharedMemoryArbiter> SharedMemoryArbiter::CreateInstance(
     SharedMemory* shared_memory,
     size_t page_size,
@@ -47,16 +66,27 @@
                                   page_size, producer_endpoint, task_runner));
 }
 
+// static
+std::unique_ptr<SharedMemoryArbiter> SharedMemoryArbiter::CreateUnboundInstance(
+    SharedMemory* shared_memory,
+    size_t page_size) {
+  return std::unique_ptr<SharedMemoryArbiterImpl>(new SharedMemoryArbiterImpl(
+      shared_memory->start(), shared_memory->size(), page_size,
+      /*producer_endpoint=*/nullptr, /*task_runner=*/nullptr));
+}
+
 SharedMemoryArbiterImpl::SharedMemoryArbiterImpl(
     void* start,
     size_t size,
     size_t page_size,
     TracingService::ProducerEndpoint* producer_endpoint,
     base::TaskRunner* task_runner)
-    : task_runner_(task_runner),
+    : initially_bound_(task_runner && producer_endpoint),
       producer_endpoint_(producer_endpoint),
+      task_runner_(task_runner),
       shmem_abi_(reinterpret_cast<uint8_t*>(start), size, page_size),
       active_writer_ids_(kMaxWriterID),
+      fully_bound_(initially_bound_),
       weak_ptr_factory_(this) {}
 
 Chunk SharedMemoryArbiterImpl::GetNewChunk(
@@ -64,8 +94,16 @@
     BufferExhaustedPolicy buffer_exhausted_policy,
     size_t size_hint) {
   PERFETTO_DCHECK(size_hint == 0);  // Not implemented yet.
+  // If initially unbound, we do not support stalling. In theory, we could
+  // support stalling for TraceWriters created after the arbiter and startup
+  // buffer reservations were bound, but to avoid raciness between the creation
+  // of startup writers and binding, we categorically forbid kStall mode.
+  PERFETTO_DCHECK(initially_bound_ ||
+                  buffer_exhausted_policy == BufferExhaustedPolicy::kDrop);
+
   int stall_count = 0;
   unsigned stall_interval_us = 0;
+  bool task_runner_runs_on_current_thread = false;
   static const unsigned kMaxStallIntervalUs = 100000;
   static const int kLogAfterNStalls = 3;
   static const int kFlushCommitsAfterEveryNStalls = 2;
@@ -78,6 +116,9 @@
     {
       std::unique_lock<std::mutex> scoped_lock(lock_);
 
+      task_runner_runs_on_current_thread =
+          task_runner_ && task_runner_->RunsTasksOnCurrentThread();
+
       // If more than half of the SMB.size() is filled with completed chunks for
       // which we haven't notified the service yet (i.e. they are still enqueued
       // in |commit_data_req_|), force a synchronous CommitDataRequest() even if
@@ -89,9 +130,9 @@
       // synchronously on another thread will lead to subtle bugs caused by
       // out-of-order commit requests (crbug.com/919187#c28).
       bool should_commit_synchronously =
+          task_runner_runs_on_current_thread &&
           buffer_exhausted_policy == BufferExhaustedPolicy::kStall &&
-          commit_data_req_ && bytes_pending_commit_ >= shmem_abi_.size() / 2 &&
-          task_runner_->RunsTasksOnCurrentThread();
+          commit_data_req_ && bytes_pending_commit_ >= shmem_abi_.size() / 2;
 
       const size_t initial_page_idx = page_idx_;
       for (size_t i = 0; i < shmem_abi_.num_pages(); i++) {
@@ -136,13 +177,15 @@
           }
         }
       }
-    }  // std::unique_lock<std::mutex>
+    }  // scoped_lock
 
     if (buffer_exhausted_policy == BufferExhaustedPolicy::kDrop) {
       PERFETTO_DLOG("Shared memory buffer exhaused, returning invalid Chunk!");
       return Chunk();
     }
 
+    PERFETTO_DCHECK(initially_bound_);
+
     // All chunks are taken (either kBeingWritten by us or kBeingRead by the
     // Service).
     if (stall_count++ == kLogAfterNStalls) {
@@ -165,7 +208,7 @@
     // is the service thread. To avoid remaining stalled forever in such a
     // situation, we attempt to flush periodically after every N stalls.
     if (stall_count % kFlushCommitsAfterEveryNStalls == 0 &&
-        task_runner_->RunsTasksOnCurrentThread()) {
+        task_runner_runs_on_current_thread) {
       // TODO(primiano): sending the IPC synchronously is a temporary workaround
       // until the backpressure logic in probes_producer is sorted out. Until
       // then the risk is that we stall the message loop waiting for the tracing
@@ -183,9 +226,10 @@
   }
 }
 
-void SharedMemoryArbiterImpl::ReturnCompletedChunk(Chunk chunk,
-                                                   BufferID target_buffer,
-                                                   PatchList* patch_list) {
+void SharedMemoryArbiterImpl::ReturnCompletedChunk(
+    Chunk chunk,
+    MaybeUnboundBufferID target_buffer,
+    PatchList* patch_list) {
   PERFETTO_DCHECK(chunk.is_valid());
   const WriterID writer_id = chunk.writer_id();
   UpdateCommitDataRequest(std::move(chunk), writer_id, target_buffer,
@@ -193,26 +237,32 @@
 }
 
 void SharedMemoryArbiterImpl::SendPatches(WriterID writer_id,
-                                          BufferID target_buffer,
+                                          MaybeUnboundBufferID target_buffer,
                                           PatchList* patch_list) {
   PERFETTO_DCHECK(!patch_list->empty() && patch_list->front().is_patched());
   UpdateCommitDataRequest(Chunk(), writer_id, target_buffer, patch_list);
 }
 
-void SharedMemoryArbiterImpl::UpdateCommitDataRequest(Chunk chunk,
-                                                      WriterID writer_id,
-                                                      BufferID target_buffer,
-                                                      PatchList* patch_list) {
+void SharedMemoryArbiterImpl::UpdateCommitDataRequest(
+    Chunk chunk,
+    WriterID writer_id,
+    MaybeUnboundBufferID target_buffer,
+    PatchList* patch_list) {
   // Note: chunk will be invalid if the call came from SendPatches().
-  bool should_post_callback = false;
+  base::TaskRunner* task_runner_to_post_callback_on = nullptr;
   base::WeakPtr<SharedMemoryArbiterImpl> weak_this;
   {
     std::lock_guard<std::mutex> scoped_lock(lock_);
 
     if (!commit_data_req_) {
       commit_data_req_.reset(new CommitDataRequest());
-      weak_this = weak_ptr_factory_.GetWeakPtr();
-      should_post_callback = true;
+
+      // Flushing the commit is only supported while we're |fully_bound_|. If we
+      // aren't, we'll flush when |fully_bound_| is updated.
+      if (fully_bound_) {
+        weak_this = weak_ptr_factory_.GetWeakPtr();
+        task_runner_to_post_callback_on = task_runner_;
+      }
     }
 
     // If a valid chunk is specified, return it and attach it to the request.
@@ -260,8 +310,10 @@
     }
   }  // scoped_lock(lock_)
 
-  if (should_post_callback) {
-    task_runner_->PostTask([weak_this] {
+  // We shouldn't post tasks while locked. |task_runner_to_post_callback_on|
+  // remains valid after unlocking, because |task_runner_| is never reset.
+  if (task_runner_to_post_callback_on) {
+    task_runner_to_post_callback_on->PostTask([weak_this] {
       if (weak_this)
         weak_this->FlushPendingCommitDataRequests();
     });
@@ -279,25 +331,50 @@
 //    crbug.com/919187 for more context.
 void SharedMemoryArbiterImpl::FlushPendingCommitDataRequests(
     std::function<void()> callback) {
-  // May be called by TraceWriterImpl on any thread.
-  if (!task_runner_->RunsTasksOnCurrentThread()) {
-    auto weak_this = weak_ptr_factory_.GetWeakPtr();
-    task_runner_->PostTask([weak_this, callback] {
-      if (weak_this)
-        weak_this->FlushPendingCommitDataRequests(std::move(callback));
-    });
-    return;
-  }
-
-  std::shared_ptr<CommitDataRequest> req;
+  std::unique_ptr<CommitDataRequest> req;
   {
-    std::lock_guard<std::mutex> scoped_lock(lock_);
-    req = std::move(commit_data_req_);
-    bytes_pending_commit_ = 0;
-  }
+    std::unique_lock<std::mutex> scoped_lock(lock_);
 
-  // |req| could be a nullptr if |commit_data_req_| became a nullptr. For
-  // example when a forced sync flush happens in GetNewChunk().
+    // Flushing is only supported while |fully_bound_|, and there may still be
+    // unbound startup trace writers. If so, skip the commit for now - it'll be
+    // done when |fully_bound_| is updated.
+    if (!fully_bound_) {
+      if (callback)
+        pending_flush_callbacks_.push_back(callback);
+      return;
+    }
+
+    // May be called by TraceWriterImpl on any thread.
+    base::TaskRunner* task_runner = task_runner_;
+    if (!task_runner->RunsTasksOnCurrentThread()) {
+      // We shouldn't post a task while holding a lock. |task_runner| remains
+      // valid after unlocking, because |task_runner_| is never reset.
+      scoped_lock.unlock();
+
+      auto weak_this = weak_ptr_factory_.GetWeakPtr();
+      task_runner->PostTask([weak_this, callback] {
+        if (weak_this)
+          weak_this->FlushPendingCommitDataRequests(std::move(callback));
+      });
+      return;
+    }
+
+    // |commit_data_req_| could have become a nullptr, for example when a forced
+    // sync flush happens in GetNewChunk().
+    if (commit_data_req_) {
+      // Make sure any placeholder buffer IDs from StartupWriters are replaced
+      // before sending the request.
+      bool all_placeholders_replaced =
+          ReplaceCommitPlaceholderBufferIdsLocked();
+      // We're |fully_bound_|, thus all writers are bound and all placeholders
+      // should have been replaced.
+      PERFETTO_DCHECK(all_placeholders_replaced);
+
+      req = std::move(commit_data_req_);
+      bytes_pending_commit_ = 0;
+    }
+  }  // scoped_lock
+
   if (req) {
     producer_endpoint_->CommitData(*req, callback);
   } else if (callback) {
@@ -312,100 +389,347 @@
 std::unique_ptr<TraceWriter> SharedMemoryArbiterImpl::CreateTraceWriter(
     BufferID target_buffer,
     BufferExhaustedPolicy buffer_exhausted_policy) {
-  WriterID id;
-  {
-    std::lock_guard<std::mutex> scoped_lock(lock_);
-    id = active_writer_ids_.Allocate();
-  }
-  if (!id)
-    return std::unique_ptr<TraceWriter>(new NullTraceWriter());
-  auto weak_this = weak_ptr_factory_.GetWeakPtr();
-  task_runner_->PostTask([weak_this, id, target_buffer] {
-    if (weak_this)
-      weak_this->producer_endpoint_->RegisterTraceWriter(id, target_buffer);
-  });
-  return std::unique_ptr<TraceWriter>(
-      new TraceWriterImpl(this, id, target_buffer, buffer_exhausted_policy));
+  PERFETTO_CHECK(target_buffer > 0);
+  return CreateTraceWriterInternal(target_buffer, buffer_exhausted_policy);
 }
 
-void SharedMemoryArbiterImpl::BindStartupTraceWriterRegistry(
-    std::unique_ptr<StartupTraceWriterRegistry> registry,
-    BufferID target_buffer) {
-  if (!task_runner_->RunsTasksOnCurrentThread()) {
+std::unique_ptr<TraceWriter> SharedMemoryArbiterImpl::CreateStartupTraceWriter(
+    uint16_t target_buffer_reservation_id) {
+  PERFETTO_CHECK(!initially_bound_);
+  return CreateTraceWriterInternal(
+      MakeTargetBufferIdForReservation(target_buffer_reservation_id),
+      BufferExhaustedPolicy::kDrop);
+}
+
+void SharedMemoryArbiterImpl::BindToProducerEndpoint(
+    TracingService::ProducerEndpoint* producer_endpoint,
+    base::TaskRunner* task_runner) {
+  PERFETTO_DCHECK(producer_endpoint && task_runner);
+  PERFETTO_DCHECK(task_runner->RunsTasksOnCurrentThread());
+  PERFETTO_CHECK(!initially_bound_);
+
+  bool should_flush = false;
+  std::function<void()> flush_callback;
+  {
+    std::lock_guard<std::mutex> scoped_lock(lock_);
+    PERFETTO_CHECK(!fully_bound_);
+    PERFETTO_CHECK(!producer_endpoint_ && !task_runner_);
+
+    producer_endpoint_ = producer_endpoint;
+    task_runner_ = task_runner;
+
+    // Now that we're bound to a task runner, also reset the WeakPtrFactory to
+    // it. Because this code runs on the task runner, the factory's weak
+    // pointers will be valid on it.
+    weak_ptr_factory_.Reset(this);
+
+    // All writers registered so far should be startup trace writers, since
+    // the producer cannot feasibly know the target buffer for any future
+    // session yet.
+    for (const auto& entry : pending_writers_) {
+      PERFETTO_CHECK(IsReservationTargetBufferId(entry.second));
+    }
+
+    // If all buffer reservations are bound, we can flush pending commits.
+    if (UpdateFullyBoundLocked()) {
+      should_flush = true;
+      flush_callback = TakePendingFlushCallbacksLocked();
+    }
+  }  // scoped_lock
+
+  // Attempt to flush any pending commits (and run pending flush callbacks). If
+  // there are none, this will have no effect. If we ended up in a race that
+  // changed |fully_bound_| back to false, the commit will happen once we become
+  // |fully_bound_| again.
+  if (should_flush)
+    FlushPendingCommitDataRequests(flush_callback);
+}
+
+void SharedMemoryArbiterImpl::BindStartupTargetBuffer(
+    uint16_t target_buffer_reservation_id,
+    BufferID target_buffer_id) {
+  PERFETTO_DCHECK(target_buffer_id > 0);
+  PERFETTO_CHECK(!initially_bound_);
+
+  std::unique_lock<std::mutex> scoped_lock(lock_);
+
+  // We should already be bound to an endpoint, but not fully bound.
+  PERFETTO_CHECK(!fully_bound_);
+  PERFETTO_CHECK(producer_endpoint_);
+  PERFETTO_CHECK(task_runner_);
+  PERFETTO_CHECK(task_runner_->RunsTasksOnCurrentThread());
+
+  BindStartupTargetBufferImpl(std::move(scoped_lock),
+                              target_buffer_reservation_id, target_buffer_id);
+}
+
+void SharedMemoryArbiterImpl::AbortStartupTracingForReservation(
+    uint16_t target_buffer_reservation_id) {
+  PERFETTO_CHECK(!initially_bound_);
+
+  std::unique_lock<std::mutex> scoped_lock(lock_);
+
+  // If we are already bound to an arbiter, we may need to flush after aborting
+  // the session, and thus should be running on the arbiter's task runner.
+  if (task_runner_ && !task_runner_->RunsTasksOnCurrentThread()) {
+    // We shouldn't post tasks while locked.
+    auto* task_runner = task_runner_;
+    scoped_lock.unlock();
+
     auto weak_this = weak_ptr_factory_.GetWeakPtr();
-    auto* raw_reg = registry.release();
-    task_runner_->PostTask([weak_this, raw_reg, target_buffer]() {
-      std::unique_ptr<StartupTraceWriterRegistry> owned_reg(raw_reg);
+    task_runner->PostTask([weak_this, target_buffer_reservation_id]() {
       if (!weak_this)
         return;
-      weak_this->BindStartupTraceWriterRegistry(std::move(owned_reg),
-                                                target_buffer);
+      weak_this->AbortStartupTracingForReservation(
+          target_buffer_reservation_id);
     });
     return;
   }
 
-  // The registry will be owned by the arbiter, so it's safe to capture |this|
-  // in the callback.
-  auto on_bound_callback = [this](StartupTraceWriterRegistry* bound_registry) {
-    std::unique_ptr<StartupTraceWriterRegistry> registry_to_delete;
-    {
-      std::lock_guard<std::mutex> scoped_lock(lock_);
+  PERFETTO_CHECK(!fully_bound_);
 
-      for (auto it = startup_trace_writer_registries_.begin();
-           it != startup_trace_writer_registries_.end(); it++) {
-        if (it->get() == bound_registry) {
-          // We can't delete the registry while the arbiter's lock is held
-          // (to avoid lock inversion).
-          registry_to_delete = std::move(*it);
-          startup_trace_writer_registries_.erase(it);
-          break;
-        }
+  // Bind the target buffer reservation to an invalid buffer (ID 0), so that
+  // existing commits, as well as future commits (of currently acquired chunks),
+  // will be released as free free by the service but otherwise ignored (i.e.
+  // not copied into any valid target buffer).
+  BindStartupTargetBufferImpl(std::move(scoped_lock),
+                              target_buffer_reservation_id,
+                              /*target_buffer_id=*/kInvalidBufferId);
+}
+
+void SharedMemoryArbiterImpl::BindStartupTargetBufferImpl(
+    std::unique_lock<std::mutex> scoped_lock,
+    uint16_t target_buffer_reservation_id,
+    BufferID target_buffer_id) {
+  // We should already be bound to an endpoint if the target buffer is valid.
+  PERFETTO_DCHECK((producer_endpoint_ && task_runner_) ||
+                  target_buffer_id == kInvalidBufferId);
+
+  MaybeUnboundBufferID reserved_id =
+      MakeTargetBufferIdForReservation(target_buffer_reservation_id);
+
+  bool should_flush = false;
+  std::function<void()> flush_callback;
+  std::vector<std::pair<WriterID, BufferID>> writers_to_register;
+
+  TargetBufferReservation& reservation =
+      target_buffer_reservations_[reserved_id];
+  PERFETTO_CHECK(!reservation.resolved);
+  reservation.resolved = true;
+  reservation.target_buffer = target_buffer_id;
+
+  // Collect trace writers associated with the reservation.
+  for (auto it = pending_writers_.begin(); it != pending_writers_.end();) {
+    if (it->second == reserved_id) {
+      // No need to register writers that have an invalid target buffer.
+      if (target_buffer_id != kInvalidBufferId) {
+        writers_to_register.push_back(
+            std::make_pair(it->first, target_buffer_id));
       }
+      it = pending_writers_.erase(it);
+    } else {
+      it++;
     }
+  }
 
-    // The registry should have been in |startup_trace_writer_registries_|.
-    PERFETTO_DCHECK(registry_to_delete);
-    registry_to_delete.reset();
+  // If all buffer reservations are bound, we can flush pending commits.
+  if (UpdateFullyBoundLocked()) {
+    should_flush = true;
+    flush_callback = TakePendingFlushCallbacksLocked();
+  }
+
+  scoped_lock.unlock();
+
+  // Register any newly bound trace writers with the service.
+  for (const auto& writer_and_target_buffer : writers_to_register) {
+    producer_endpoint_->RegisterTraceWriter(writer_and_target_buffer.first,
+                                            writer_and_target_buffer.second);
+  }
+
+  // Attempt to flush any pending commits (and run pending flush callbacks). If
+  // there are none, this will have no effect. If we ended up in a race that
+  // changed |fully_bound_| back to false, the commit will happen once we become
+  // |fully_bound_| again.
+  if (should_flush)
+    FlushPendingCommitDataRequests(flush_callback);
+}
+
+std::function<void()>
+SharedMemoryArbiterImpl::TakePendingFlushCallbacksLocked() {
+  if (pending_flush_callbacks_.empty())
+    return std::function<void()>();
+
+  std::vector<std::function<void()>> pending_flush_callbacks;
+  pending_flush_callbacks.swap(pending_flush_callbacks_);
+  // Capture the callback list into the lambda by copy.
+  return [pending_flush_callbacks]() {
+    for (auto& callback : pending_flush_callbacks)
+      callback();
   };
-  registry->BindToArbiter(this, target_buffer, task_runner_, on_bound_callback);
-  std::lock_guard<std::mutex> scoped_lock(lock_);
-  startup_trace_writer_registries_.push_back(std::move(registry));
 }
 
 void SharedMemoryArbiterImpl::NotifyFlushComplete(FlushRequestID req_id) {
-  bool should_post_commit_task = false;
+  base::TaskRunner* task_runner_to_commit_on = nullptr;
+
   {
     std::lock_guard<std::mutex> scoped_lock(lock_);
     // If a commit_data_req_ exists it means that somebody else already posted a
     // FlushPendingCommitDataRequests() task.
     if (!commit_data_req_) {
       commit_data_req_.reset(new CommitDataRequest());
-      should_post_commit_task = true;
+
+      // Flushing the commit is only supported while we're |fully_bound_|. If we
+      // aren't, we'll flush when |fully_bound_| is updated.
+      if (fully_bound_)
+        task_runner_to_commit_on = task_runner_;
     } else {
       // If there is another request queued and that also contains is a reply
       // to a flush request, reply with the highest id.
       req_id = std::max(req_id, commit_data_req_->flush_request_id());
     }
     commit_data_req_->set_flush_request_id(req_id);
-  }
-  if (should_post_commit_task) {
+  }  // scoped_lock
+
+  // We shouldn't post tasks while locked. |task_runner_to_commit_on|
+  // remains valid after unlocking, because |task_runner_| is never reset.
+  if (task_runner_to_commit_on) {
     auto weak_this = weak_ptr_factory_.GetWeakPtr();
-    task_runner_->PostTask([weak_this] {
+    task_runner_to_commit_on->PostTask([weak_this] {
       if (weak_this)
         weak_this->FlushPendingCommitDataRequests();
     });
   }
 }
 
+std::unique_ptr<TraceWriter> SharedMemoryArbiterImpl::CreateTraceWriterInternal(
+    MaybeUnboundBufferID target_buffer,
+    BufferExhaustedPolicy buffer_exhausted_policy) {
+  WriterID id;
+  base::TaskRunner* task_runner_to_register_on = nullptr;
+
+  {
+    std::lock_guard<std::mutex> scoped_lock(lock_);
+    id = active_writer_ids_.Allocate();
+
+    if (!id)
+      return std::unique_ptr<TraceWriter>(new NullTraceWriter());
+
+    PERFETTO_DCHECK(!pending_writers_.count(id));
+
+    if (IsReservationTargetBufferId(target_buffer)) {
+      // If the reservation is new, mark it as unbound in
+      // |target_buffer_reservations_|. Otherwise, if the reservation was
+      // already bound, choose the bound buffer ID now.
+      auto it_and_inserted = target_buffer_reservations_.insert(
+          {target_buffer, TargetBufferReservation()});
+      if (it_and_inserted.first->second.resolved)
+        target_buffer = it_and_inserted.first->second.target_buffer;
+    }
+
+    if (IsReservationTargetBufferId(target_buffer)) {
+      // The arbiter and/or startup buffer reservations are not bound yet, so
+      // buffer the registration of the writer until after we're bound.
+      pending_writers_[id] = target_buffer;
+
+      // Mark the arbiter as not fully bound, since we now have at least one
+      // unbound trace writer / target buffer reservation.
+      fully_bound_ = false;
+    } else if (target_buffer != kInvalidBufferId) {
+      // Trace writer is bound, so arbiter should be bound to an endpoint, too.
+      PERFETTO_CHECK(producer_endpoint_ && task_runner_);
+      task_runner_to_register_on = task_runner_;
+    }
+  }  // scoped_lock
+
+  // We shouldn't post tasks while locked. |task_runner_to_register_on|
+  // remains valid after unlocking, because |task_runner_| is never reset.
+  if (task_runner_to_register_on) {
+    auto weak_this = weak_ptr_factory_.GetWeakPtr();
+    task_runner_to_register_on->PostTask([weak_this, id, target_buffer] {
+      if (weak_this)
+        weak_this->producer_endpoint_->RegisterTraceWriter(id, target_buffer);
+    });
+  }
+
+  return std::unique_ptr<TraceWriter>(
+      new TraceWriterImpl(this, id, target_buffer, buffer_exhausted_policy));
+}
+
 void SharedMemoryArbiterImpl::ReleaseWriterID(WriterID id) {
+  base::TaskRunner* task_runner = nullptr;
+  {
+    std::lock_guard<std::mutex> scoped_lock(lock_);
+    active_writer_ids_.Free(id);
+
+    auto it = pending_writers_.find(id);
+    if (it != pending_writers_.end()) {
+      // Writer hasn't been bound yet and thus also not yet registered with the
+      // service.
+      pending_writers_.erase(it);
+      return;
+    }
+
+    // A trace writer from an aborted session may be destroyed before the
+    // arbiter is bound to a task runner. In that case, it was never registered
+    // with the service.
+    if (!task_runner_)
+      return;
+
+    task_runner = task_runner_;
+  }  // scoped_lock
+
+  // We shouldn't post tasks while locked. |task_runner| remains valid after
+  // unlocking, because |task_runner_| is never reset.
   auto weak_this = weak_ptr_factory_.GetWeakPtr();
-  task_runner_->PostTask([weak_this, id] {
+  task_runner->PostTask([weak_this, id] {
     if (weak_this)
       weak_this->producer_endpoint_->UnregisterTraceWriter(id);
   });
+}
 
-  std::lock_guard<std::mutex> scoped_lock(lock_);
-  active_writer_ids_.Free(id);
+bool SharedMemoryArbiterImpl::ReplaceCommitPlaceholderBufferIdsLocked() {
+  if (!commit_data_req_)
+    return true;
+
+  bool all_placeholders_replaced = true;
+  for (auto& chunk : *commit_data_req_->mutable_chunks_to_move()) {
+    if (!IsReservationTargetBufferId(chunk.target_buffer()))
+      continue;
+    const auto it = target_buffer_reservations_.find(chunk.target_buffer());
+    PERFETTO_DCHECK(it != target_buffer_reservations_.end());
+    if (!it->second.resolved) {
+      all_placeholders_replaced = false;
+      continue;
+    }
+    chunk.set_target_buffer(it->second.target_buffer);
+  }
+  for (auto& chunk : *commit_data_req_->mutable_chunks_to_patch()) {
+    if (!IsReservationTargetBufferId(chunk.target_buffer()))
+      continue;
+    const auto it = target_buffer_reservations_.find(chunk.target_buffer());
+    PERFETTO_DCHECK(it != target_buffer_reservations_.end());
+    if (!it->second.resolved) {
+      all_placeholders_replaced = false;
+      continue;
+    }
+    chunk.set_target_buffer(it->second.target_buffer);
+  }
+  return all_placeholders_replaced;
+}
+
+bool SharedMemoryArbiterImpl::UpdateFullyBoundLocked() {
+  if (!producer_endpoint_) {
+    PERFETTO_DCHECK(!fully_bound_);
+    return false;
+  }
+  // We're fully bound if all target buffer reservations have a valid associated
+  // BufferID.
+  fully_bound_ = std::none_of(
+      target_buffer_reservations_.begin(), target_buffer_reservations_.end(),
+      [](std::pair<MaybeUnboundBufferID, TargetBufferReservation> entry) {
+        return !entry.second.resolved;
+      });
+  return fully_bound_;
 }
 
 }  // namespace perfetto
diff --git a/src/tracing/core/shared_memory_arbiter_impl.h b/src/tracing/core/shared_memory_arbiter_impl.h
index 22bf09d..766059c 100644
--- a/src/tracing/core/shared_memory_arbiter_impl.h
+++ b/src/tracing/core/shared_memory_arbiter_impl.h
@@ -20,6 +20,7 @@
 #include <stdint.h>
 
 #include <functional>
+#include <map>
 #include <memory>
 #include <mutex>
 #include <vector>
@@ -28,9 +29,9 @@
 #include "perfetto/ext/tracing/core/basic_types.h"
 #include "perfetto/ext/tracing/core/shared_memory_abi.h"
 #include "perfetto/ext/tracing/core/shared_memory_arbiter.h"
-#include "perfetto/ext/tracing/core/startup_trace_writer_registry.h"
 #include "perfetto/tracing/core/forward_decls.h"
 #include "src/tracing/core/id_allocator.h"
+
 namespace perfetto {
 
 class PatchList;
@@ -47,10 +48,52 @@
 // This class is thread-safe and uses locks to do so. Data sources are supposed
 // to interact with this sporadically, only when they run out of space on their
 // current thread-local chunk.
+//
+// When the arbiter is created using CreateUnboundInstance(), the following
+// state transitions are possible:
+//
+//   [ !fully_bound_, !endpoint_, 0 unbound buffer reservations ]
+//       |     |
+//       |     | CreateStartupTraceWriter(buf)
+//       |     |  buffer reservations += buf
+//       |     |
+//       |     |             ----
+//       |     |            |    | CreateStartupTraceWriter(buf)
+//       |     |            |    |  buffer reservations += buf
+//       |     V            |    V
+//       |   [ !fully_bound_, !endpoint_, >=1 unbound buffer reservations ]
+//       |                                                |
+//       |                       BindToProducerEndpoint() |
+//       |                                                |
+//       | BindToProducerEndpoint()                       |
+//       |                                                V
+//       |   [ !fully_bound_, endpoint_, >=1 unbound buffer reservations ]
+//       |   A    |    A                               |     A
+//       |   |    |    |                               |     |
+//       |   |     ----                                |     |
+//       |   |    CreateStartupTraceWriter(buf)        |     |
+//       |   |     buffer reservations += buf          |     |
+//       |   |                                         |     |
+//       |   | CreateStartupTraceWriter(buf)           |     |
+//       |   |  where buf is not yet bound             |     |
+//       |   |  buffer reservations += buf             |     | (yes)
+//       |   |                                         |     |
+//       |   |        BindStartupTargetBuffer(buf, id) |-----
+//       |   |           buffer reservations -= buf    | reservations > 0?
+//       |   |                                         |
+//       |   |                                         | (no)
+//       V   |                                         V
+//       [ fully_bound_, endpoint_, 0 unbound buffer reservations ]
+//          |    A
+//          |    | CreateStartupTraceWriter(buf)
+//          |    |  where buf is already bound
+//           ----
 class SharedMemoryArbiterImpl : public SharedMemoryArbiter {
  public:
   // See SharedMemoryArbiter::CreateInstance(). |start|, |size| define the
-  // boundaries of the shared memory buffer.
+  // boundaries of the shared memory buffer. ProducerEndpoint and TaskRunner may
+  // be |nullptr| if created unbound, see
+  // SharedMemoryArbiter::CreateUnboundInstance().
   SharedMemoryArbiterImpl(void* start,
                           size_t size,
                           size_t page_size,
@@ -73,14 +116,14 @@
   // first patched entries will be removed from the patched list and sent over
   // to the service in the same CommitData() IPC request.
   void ReturnCompletedChunk(SharedMemoryABI::Chunk,
-                            BufferID target_buffer,
+                            MaybeUnboundBufferID target_buffer,
                             PatchList*);
 
   // Send a request to the service to apply completed patches from |patch_list|.
   // |writer_id| is the ID of the TraceWriter that calls this method,
   // |target_buffer| is the global trace buffer ID of its target buffer.
   void SendPatches(WriterID writer_id,
-                   BufferID target_buffer,
+                   MaybeUnboundBufferID target_buffer,
                    PatchList* patch_list);
 
   // Forces a synchronous commit of the completed packets without waiting for
@@ -98,10 +141,14 @@
   std::unique_ptr<TraceWriter> CreateTraceWriter(
       BufferID target_buffer,
       BufferExhaustedPolicy = BufferExhaustedPolicy::kDefault) override;
-  void BindStartupTraceWriterRegistry(
-      std::unique_ptr<StartupTraceWriterRegistry>,
-      BufferID target_buffer) override;
-
+  std::unique_ptr<TraceWriter> CreateStartupTraceWriter(
+      uint16_t target_buffer_reservation_id) override;
+  void BindToProducerEndpoint(TracingService::ProducerEndpoint*,
+                              base::TaskRunner*) override;
+  void BindStartupTargetBuffer(uint16_t target_buffer_reservation_id,
+                               BufferID target_buffer_id) override;
+  void AbortStartupTracingForReservation(
+      uint16_t target_buffer_reservation_id) override;
   void NotifyFlushComplete(FlushRequestID) override;
 
   base::TaskRunner* task_runner() const { return task_runner_; }
@@ -115,6 +162,16 @@
  private:
   friend class TraceWriterImpl;
   friend class StartupTraceWriterTest;
+  friend class SharedMemoryArbiterImplTest;
+
+  struct TargetBufferReservation {
+    bool resolved = false;
+    BufferID target_buffer = kInvalidBufferId;
+  };
+
+  // Placeholder for the actual target buffer ID of a startup target buffer
+  // reservation ID in |target_buffer_reservations_|.
+  static constexpr BufferID kInvalidBufferId = 0;
 
   static SharedMemoryABI::PageLayout default_page_layout;
 
@@ -123,26 +180,75 @@
 
   void UpdateCommitDataRequest(SharedMemoryABI::Chunk chunk,
                                WriterID writer_id,
-                               BufferID target_buffer,
+                               MaybeUnboundBufferID target_buffer,
                                PatchList* patch_list);
 
+  std::unique_ptr<TraceWriter> CreateTraceWriterInternal(
+      MaybeUnboundBufferID target_buffer,
+      BufferExhaustedPolicy);
+
   // Called by the TraceWriter destructor.
   void ReleaseWriterID(WriterID);
 
-  base::TaskRunner* const task_runner_;
-  TracingService::ProducerEndpoint* const producer_endpoint_;
+  void BindStartupTargetBufferImpl(std::unique_lock<std::mutex> scoped_lock,
+                                   uint16_t target_buffer_reservation_id,
+                                   BufferID target_buffer_id);
+
+  // If any flush callbacks were queued up while the arbiter or any target
+  // buffer reservation was unbound, this wraps the pending callbacks into a new
+  // std::function and returns it. Otherwise returns an invalid std::function.
+  std::function<void()> TakePendingFlushCallbacksLocked();
+
+  // Replace occurrences of target buffer reservation IDs in |commit_data_req_|
+  // with their respective actual BufferIDs if they were already bound. Returns
+  // true iff all occurrences were replaced.
+  bool ReplaceCommitPlaceholderBufferIdsLocked();
+
+  // Update and return |fully_bound_| based on the arbiter's |pending_writers_|
+  // state.
+  bool UpdateFullyBoundLocked();
+
+  const bool initially_bound_;
+  // Only accessed on |task_runner_| after the producer endpoint was bound.
+  TracingService::ProducerEndpoint* producer_endpoint_ = nullptr;
 
   // --- Begin lock-protected members ---
+
   std::mutex lock_;
+
+  base::TaskRunner* task_runner_ = nullptr;
   SharedMemoryABI shmem_abi_;
   size_t page_idx_ = 0;
   std::unique_ptr<CommitDataRequest> commit_data_req_;
   size_t bytes_pending_commit_ = 0;  // SUM(chunk.size() : commit_data_req_).
   IdAllocator<WriterID> active_writer_ids_;
-  // Registries whose Bind() is in progress. We destroy each registry when their
-  // Bind() is complete or when the arbiter is destroyed itself.
-  std::vector<std::unique_ptr<StartupTraceWriterRegistry>>
-      startup_trace_writer_registries_;
+
+  // Whether the arbiter itself and all startup target buffer reservations are
+  // bound. Note that this can become false again later if a new target buffer
+  // reservation is created by calling CreateStartupTraceWriter() with a new
+  // reservation id.
+  bool fully_bound_;
+
+  // IDs of writers and their assigned target buffers that should be registered
+  // with the service after the arbiter and/or their startup target buffer is
+  // bound.
+  std::map<WriterID, MaybeUnboundBufferID> pending_writers_;
+
+  // Callbacks for flush requests issued while the arbiter or a target buffer
+  // reservation was unbound.
+  std::vector<std::function<void()>> pending_flush_callbacks_;
+
+  // Stores target buffer reservations for writers created via
+  // CreateStartupTraceWriter(). A bound reservation sets
+  // TargetBufferReservation::resolved to true and is associated with the actual
+  // BufferID supplied in BindStartupTargetBuffer().
+  //
+  // TODO(eseckler): Clean up entries from this map. This would probably require
+  // a method in SharedMemoryArbiter that allows a producer to invalidate a
+  // reservation ID.
+  std::map<MaybeUnboundBufferID, TargetBufferReservation>
+      target_buffer_reservations_;
+
   // --- End lock-protected members ---
 
   // Keep at the end.
diff --git a/src/tracing/core/shared_memory_arbiter_impl_unittest.cc b/src/tracing/core/shared_memory_arbiter_impl_unittest.cc
index 91bd5db..0326bcb 100644
--- a/src/tracing/core/shared_memory_arbiter_impl_unittest.cc
+++ b/src/tracing/core/shared_memory_arbiter_impl_unittest.cc
@@ -21,6 +21,7 @@
 #include "perfetto/ext/tracing/core/basic_types.h"
 #include "perfetto/ext/tracing/core/commit_data_request.h"
 #include "perfetto/ext/tracing/core/shared_memory_abi.h"
+#include "perfetto/ext/tracing/core/trace_packet.h"
 #include "perfetto/ext/tracing/core/trace_writer.h"
 #include "src/base/test/gtest_test_suite.h"
 #include "src/base/test/test_task_runner.h"
@@ -29,8 +30,10 @@
 #include "src/tracing/test/fake_producer_endpoint.h"
 #include "test/gtest_and_gmock.h"
 
+#include "protos/perfetto/trace/test_event.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
 namespace perfetto {
-namespace {
 
 using testing::Invoke;
 using testing::_;
@@ -43,6 +46,7 @@
   void NotifyDataSourceStarted(DataSourceInstanceID) override {}
   void NotifyDataSourceStopped(DataSourceInstanceID) override {}
   void ActivateTriggers(const std::vector<std::string>&) {}
+  void Sync(std::function<void()>) override {}
   SharedMemory* shared_memory() const override { return nullptr; }
   size_t shared_buffer_page_size_kb() const override { return 0; }
   std::unique_ptr<TraceWriter> CreateTraceWriter(
@@ -50,7 +54,8 @@
       BufferExhaustedPolicy) override {
     return nullptr;
   }
-  SharedMemoryArbiter* GetInProcessShmemArbiter() override { return nullptr; }
+  SharedMemoryArbiter* MaybeSharedMemoryArbiter() override { return nullptr; }
+  bool IsShmemProvidedByProducer() const override { return false; }
 
   MOCK_METHOD2(CommitData, void(const CommitDataRequest&, CommitDataCallback));
   MOCK_METHOD2(RegisterTraceWriter, void(uint32_t, uint32_t));
@@ -67,6 +72,8 @@
                                                task_runner_.get()));
   }
 
+  bool IsArbiterFullyBound() { return arbiter_->fully_bound_; }
+
   void TearDown() override {
     arbiter_.reset();
     task_runner_.reset();
@@ -108,7 +115,7 @@
         for (size_t i = 0; i < 14 * 2; i++) {
           ASSERT_EQ(i / 14, req.chunks_to_move()[i].page());
           ASSERT_EQ((i % 14) ^ 1, req.chunks_to_move()[i].chunk());
-          ASSERT_EQ(i % 5, req.chunks_to_move()[i].target_buffer());
+          ASSERT_EQ(i % 5 + 1, req.chunks_to_move()[i].target_buffer());
         }
         ASSERT_EQ(2u, req.chunks_to_move()[28].page());
         ASSERT_EQ(1u, req.chunks_to_move()[28].chunk());
@@ -116,8 +123,10 @@
         on_commit_1();
       }));
   PatchList ignored;
-  for (size_t i = 0; i < 14 * 2; i++)
-    arbiter_->ReturnCompletedChunk(std::move(chunks[i ^ 1]), i % 5, &ignored);
+  for (size_t i = 0; i < 14 * 2; i++) {
+    arbiter_->ReturnCompletedChunk(std::move(chunks[i ^ 1]), i % 5 + 1,
+                                   &ignored);
+  }
   arbiter_->ReturnCompletedChunk(std::move(chunks[29]), 42, &ignored);
   task_runner_->RunUntilCheckpoint("on_commit_1");
 
@@ -184,7 +193,7 @@
     std::map<WriterID, std::unique_ptr<TraceWriter>> writers;
 
     for (size_t i = 0; i < kMaxWriterID; i++) {
-      std::unique_ptr<TraceWriter> writer = arbiter_->CreateTraceWriter(0);
+      std::unique_ptr<TraceWriter> writer = arbiter_->CreateTraceWriter(1);
       ASSERT_TRUE(writer);
       WriterID writer_id = writer->writer_id();
       ASSERT_TRUE(writers.emplace(writer_id, std::move(writer)).second);
@@ -192,7 +201,7 @@
 
     // A further call should return a null impl of trace writer as we exhausted
     // writer IDs.
-    ASSERT_EQ(arbiter_->CreateTraceWriter(0)->writer_id(), 0);
+    ASSERT_EQ(arbiter_->CreateTraceWriter(1)->writer_id(), 0);
   }
 
   // This should run the Register/UnregisterTraceWriter tasks enqueued by the
@@ -223,7 +232,7 @@
 
   // Returning the chunk is not enough to be able to reacquire it.
   PatchList ignored;
-  arbiter_->ReturnCompletedChunk(std::move(chunks[0]), 0, &ignored);
+  arbiter_->ReturnCompletedChunk(std::move(chunks[0]), 1, &ignored);
 
   invalid_chunk = arbiter_->GetNewChunk({}, BufferExhaustedPolicy::kDrop);
   ASSERT_FALSE(invalid_chunk.is_valid());
@@ -238,7 +247,323 @@
   ASSERT_TRUE(chunks[0].is_valid());
 }
 
+TEST_P(SharedMemoryArbiterImplTest, CreateUnboundAndBind) {
+  auto checkpoint_writer = task_runner_->CreateCheckpoint("writer_registered");
+  auto checkpoint_flush = task_runner_->CreateCheckpoint("flush_completed");
+
+  // Create an unbound arbiter and bind immediately.
+  arbiter_.reset(new SharedMemoryArbiterImpl(buf(), buf_size(), page_size(),
+                                             nullptr, nullptr));
+  arbiter_->BindToProducerEndpoint(&mock_producer_endpoint_,
+                                   task_runner_.get());
+  EXPECT_TRUE(IsArbiterFullyBound());
+
+  // Trace writer should be registered in a non-delayed task.
+  EXPECT_CALL(mock_producer_endpoint_, RegisterTraceWriter(_, 1))
+      .WillOnce(testing::InvokeWithoutArgs(checkpoint_writer));
+  std::unique_ptr<TraceWriter> writer = arbiter_->CreateTraceWriter(1);
+  task_runner_->RunUntilCheckpoint("writer_registered", 5000);
+
+  // Commits/flushes should be sent right away.
+  EXPECT_CALL(mock_producer_endpoint_, CommitData(_, _))
+      .WillOnce(testing::InvokeArgument<1>());
+  writer->Flush(checkpoint_flush);
+  task_runner_->RunUntilCheckpoint("flush_completed", 5000);
+}
+
+TEST_P(SharedMemoryArbiterImplTest, StartupTracing) {
+  constexpr uint16_t kTargetBufferReservationId1 = 1;
+  constexpr uint16_t kTargetBufferReservationId2 = 2;
+
+  // Create an unbound arbiter and a startup writer.
+  arbiter_.reset(new SharedMemoryArbiterImpl(buf(), buf_size(), page_size(),
+                                             nullptr, nullptr));
+  std::unique_ptr<TraceWriter> writer =
+      arbiter_->CreateStartupTraceWriter(kTargetBufferReservationId1);
+
+  // Write two packet while unbound and flush the chunk after each packet. The
+  // writer will return the chunk to the arbiter and grab a new chunk for the
+  // second packet. The flush should only add the chunk into the queued commit
+  // request.
+  for (int i = 0; i < 2; i++) {
+    {
+      auto packet = writer->NewTracePacket();
+      packet->set_for_testing()->set_str("foo");
+    }
+    writer->Flush();
+  }
+
+  // Bind to producer endpoint. This should not register the trace writer yet,
+  // because it's buffer reservation is still unbound.
+  arbiter_->BindToProducerEndpoint(&mock_producer_endpoint_,
+                                   task_runner_.get());
+  EXPECT_FALSE(IsArbiterFullyBound());
+
+  // Write another packet into another chunk and queue it.
+  {
+    auto packet = writer->NewTracePacket();
+    packet->set_for_testing()->set_str("foo");
+  }
+  bool flush_completed = false;
+  writer->Flush([&flush_completed] { flush_completed = true; });
+
+  // Bind the buffer reservation to a buffer. Trace writer should be registered
+  // and queued commits flushed.
+  EXPECT_CALL(mock_producer_endpoint_, RegisterTraceWriter(_, 42));
+  EXPECT_CALL(mock_producer_endpoint_, CommitData(_, _))
+      .WillOnce(Invoke([](const CommitDataRequest& req,
+                          MockProducerEndpoint::CommitDataCallback callback) {
+        ASSERT_EQ(3, req.chunks_to_move_size());
+        EXPECT_EQ(42u, req.chunks_to_move()[0].target_buffer());
+        EXPECT_EQ(42u, req.chunks_to_move()[1].target_buffer());
+        EXPECT_EQ(42u, req.chunks_to_move()[2].target_buffer());
+        callback();
+      }));
+
+  arbiter_->BindStartupTargetBuffer(kTargetBufferReservationId1, 42);
+  EXPECT_TRUE(IsArbiterFullyBound());
+
+  testing::Mock::VerifyAndClearExpectations(&mock_producer_endpoint_);
+  EXPECT_TRUE(flush_completed);
+
+  // Creating a new startup writer for the same buffer posts an immediate task
+  // to register it.
+  auto checkpoint_register1b =
+      task_runner_->CreateCheckpoint("writer1b_registered");
+  EXPECT_CALL(mock_producer_endpoint_, RegisterTraceWriter(_, 42))
+      .WillOnce(testing::InvokeWithoutArgs(checkpoint_register1b));
+  std::unique_ptr<TraceWriter> writer1b =
+      arbiter_->CreateStartupTraceWriter(kTargetBufferReservationId1);
+  task_runner_->RunUntilCheckpoint("writer1b_registered", 5000);
+
+  // And a commit on this new writer should be flushed to the right buffer, too.
+  EXPECT_CALL(mock_producer_endpoint_, CommitData(_, _))
+      .WillOnce(Invoke([](const CommitDataRequest& req,
+                          MockProducerEndpoint::CommitDataCallback callback) {
+        ASSERT_EQ(1, req.chunks_to_move_size());
+        EXPECT_EQ(42u, req.chunks_to_move()[0].target_buffer());
+        callback();
+      }));
+  {
+    auto packet = writer1b->NewTracePacket();
+    packet->set_for_testing()->set_str("foo");
+  }
+  flush_completed = false;
+  writer1b->Flush([&flush_completed] { flush_completed = true; });
+
+  testing::Mock::VerifyAndClearExpectations(&mock_producer_endpoint_);
+  EXPECT_TRUE(flush_completed);
+
+  // Create another startup writer for another target buffer, which puts the
+  // arbiter back into unbound state.
+  std::unique_ptr<TraceWriter> writer2 =
+      arbiter_->CreateStartupTraceWriter(kTargetBufferReservationId2);
+  EXPECT_FALSE(IsArbiterFullyBound());
+
+  // Write a chunk into both writers. Both should be queued up into the next
+  // commit request.
+  {
+    auto packet = writer->NewTracePacket();
+    packet->set_for_testing()->set_str("foo");
+  }
+  writer->Flush();
+  {
+    auto packet = writer2->NewTracePacket();
+    packet->set_for_testing()->set_str("bar");
+  }
+  flush_completed = false;
+  writer2->Flush([&flush_completed] { flush_completed = true; });
+
+  // Destroy the first trace writer, which should cause the arbiter to post a
+  // task to unregister it.
+  auto checkpoint_writer =
+      task_runner_->CreateCheckpoint("writer_unregistered");
+  EXPECT_CALL(mock_producer_endpoint_,
+              UnregisterTraceWriter(writer->writer_id()))
+      .WillOnce(testing::InvokeWithoutArgs(checkpoint_writer));
+  writer.reset();
+  task_runner_->RunUntilCheckpoint("writer_unregistered", 5000);
+
+  // Bind the second buffer reservation to a buffer. Second trace writer should
+  // be registered and queued commits flushed.
+  EXPECT_CALL(mock_producer_endpoint_, RegisterTraceWriter(_, 23));
+  EXPECT_CALL(mock_producer_endpoint_, CommitData(_, _))
+      .WillOnce(Invoke([](const CommitDataRequest& req,
+                          MockProducerEndpoint::CommitDataCallback callback) {
+        ASSERT_EQ(2, req.chunks_to_move_size());
+        EXPECT_EQ(42u, req.chunks_to_move()[0].target_buffer());
+        EXPECT_EQ(23u, req.chunks_to_move()[1].target_buffer());
+        callback();
+      }));
+
+  arbiter_->BindStartupTargetBuffer(kTargetBufferReservationId2, 23);
+  EXPECT_TRUE(IsArbiterFullyBound());
+
+  testing::Mock::VerifyAndClearExpectations(&mock_producer_endpoint_);
+  EXPECT_TRUE(flush_completed);
+}
+
+TEST_P(SharedMemoryArbiterImplTest, AbortStartupTracingForReservation) {
+  constexpr uint16_t kTargetBufferReservationId1 = 1;
+  constexpr uint16_t kTargetBufferReservationId2 = 2;
+
+  // Create an unbound arbiter and a startup writer.
+  arbiter_.reset(new SharedMemoryArbiterImpl(buf(), buf_size(), page_size(),
+                                             nullptr, nullptr));
+  SharedMemoryABI* shmem_abi = arbiter_->shmem_abi_for_testing();
+  std::unique_ptr<TraceWriter> writer =
+      arbiter_->CreateStartupTraceWriter(kTargetBufferReservationId1);
+  std::unique_ptr<TraceWriter> writer2 =
+      arbiter_->CreateStartupTraceWriter(kTargetBufferReservationId1);
+
+  // Write two packet while unbound and flush the chunk after each packet. The
+  // writer will return the chunk to the arbiter and grab a new chunk for the
+  // second packet. The flush should only add the chunk into the queued commit
+  // request.
+  for (int i = 0; i < 2; i++) {
+    {
+      auto packet = writer->NewTracePacket();
+      packet->set_for_testing()->set_str("foo");
+    }
+    writer->Flush();
+  }
+
+  // Abort the first session. This should clear resolve the two chunks committed
+  // up to this point to an invalid target buffer (ID 0). They will remain
+  // buffered until bound to an endpoint.
+  arbiter_->AbortStartupTracingForReservation(kTargetBufferReservationId1);
+
+  // Destroy a writer that was created before the abort. This should not cause
+  // crashes.
+  writer2.reset();
+
+  // Bind to producer endpoint. The trace writer should not be registered as its
+  // target buffer is invalid. Since no startup sessions are active anymore, the
+  // arbiter should be fully bound. The commit data request is flushed.
+  EXPECT_CALL(mock_producer_endpoint_, RegisterTraceWriter(_, _)).Times(0);
+  EXPECT_CALL(mock_producer_endpoint_, CommitData(_, _))
+      .WillOnce(Invoke([shmem_abi](const CommitDataRequest& req,
+                                   MockProducerEndpoint::CommitDataCallback) {
+        ASSERT_EQ(2, req.chunks_to_move_size());
+        for (size_t i = 0; i < 2; i++) {
+          EXPECT_EQ(0u, req.chunks_to_move()[i].target_buffer());
+          SharedMemoryABI::Chunk chunk = shmem_abi->TryAcquireChunkForReading(
+              req.chunks_to_move()[i].page(), req.chunks_to_move()[i].chunk());
+          shmem_abi->ReleaseChunkAsFree(std::move(chunk));
+        }
+      }));
+  arbiter_->BindToProducerEndpoint(&mock_producer_endpoint_,
+                                   task_runner_.get());
+  EXPECT_TRUE(IsArbiterFullyBound());
+
+  // SMB should be free again, as no writer holds on to any chunk anymore.
+  for (size_t i = 0; i < shmem_abi->num_pages(); i++)
+    EXPECT_TRUE(shmem_abi->is_page_free(i));
+
+  // Write another packet into another chunk and commit it. It should be sent
+  // to the arbiter with invalid target buffer (ID 0).
+  {
+    auto packet = writer->NewTracePacket();
+    packet->set_for_testing()->set_str("foo");
+  }
+  EXPECT_CALL(mock_producer_endpoint_, CommitData(_, _))
+      .WillOnce(Invoke([shmem_abi](
+                           const CommitDataRequest& req,
+                           MockProducerEndpoint::CommitDataCallback callback) {
+        ASSERT_EQ(1, req.chunks_to_move_size());
+        EXPECT_EQ(0u, req.chunks_to_move()[0].target_buffer());
+        SharedMemoryABI::Chunk chunk = shmem_abi->TryAcquireChunkForReading(
+            req.chunks_to_move()[0].page(), req.chunks_to_move()[0].chunk());
+        shmem_abi->ReleaseChunkAsFree(std::move(chunk));
+        callback();
+      }));
+  bool flush_completed = false;
+  writer->Flush([&flush_completed] { flush_completed = true; });
+  EXPECT_TRUE(flush_completed);
+
+  // Creating a new startup writer for the same buffer does not cause it to
+  // register.
+  EXPECT_CALL(mock_producer_endpoint_, RegisterTraceWriter(_, _)).Times(0);
+  std::unique_ptr<TraceWriter> writer1b =
+      arbiter_->CreateStartupTraceWriter(kTargetBufferReservationId1);
+
+  // And a commit on this new writer should again be flushed to the invalid
+  // target buffer.
+  {
+    auto packet = writer1b->NewTracePacket();
+    packet->set_for_testing()->set_str("foo");
+  }
+  EXPECT_CALL(mock_producer_endpoint_, CommitData(_, _))
+      .WillOnce(Invoke([shmem_abi](
+                           const CommitDataRequest& req,
+                           MockProducerEndpoint::CommitDataCallback callback) {
+        ASSERT_EQ(1, req.chunks_to_move_size());
+        EXPECT_EQ(0u, req.chunks_to_move()[0].target_buffer());
+        SharedMemoryABI::Chunk chunk = shmem_abi->TryAcquireChunkForReading(
+            req.chunks_to_move()[0].page(), req.chunks_to_move()[0].chunk());
+        shmem_abi->ReleaseChunkAsFree(std::move(chunk));
+        callback();
+      }));
+  flush_completed = false;
+  writer1b->Flush([&flush_completed] { flush_completed = true; });
+  EXPECT_TRUE(flush_completed);
+
+  // Create another startup writer for another target buffer, which puts the
+  // arbiter back into unbound state.
+  std::unique_ptr<TraceWriter> writer3 =
+      arbiter_->CreateStartupTraceWriter(kTargetBufferReservationId2);
+  EXPECT_FALSE(IsArbiterFullyBound());
+
+  // Write a chunk into both writers. Both should be queued up into the next
+  // commit request.
+  {
+    auto packet = writer->NewTracePacket();
+    packet->set_for_testing()->set_str("foo");
+  }
+  writer->Flush();
+  {
+    auto packet = writer3->NewTracePacket();
+    packet->set_for_testing()->set_str("bar");
+  }
+  flush_completed = false;
+  writer3->Flush([&flush_completed] { flush_completed = true; });
+
+  // Destroy the first trace writer, which should cause the arbiter to post a
+  // task to unregister it.
+  auto checkpoint_writer =
+      task_runner_->CreateCheckpoint("writer_unregistered");
+  EXPECT_CALL(mock_producer_endpoint_,
+              UnregisterTraceWriter(writer->writer_id()))
+      .WillOnce(testing::InvokeWithoutArgs(checkpoint_writer));
+  writer.reset();
+  task_runner_->RunUntilCheckpoint("writer_unregistered", 5000);
+
+  // Abort the second session. Its commits should now also be associated with
+  // target buffer 0, and both writers' commits flushed.
+  EXPECT_CALL(mock_producer_endpoint_, RegisterTraceWriter(_, _)).Times(0);
+  EXPECT_CALL(mock_producer_endpoint_, CommitData(_, _))
+      .WillOnce(Invoke([shmem_abi](
+                           const CommitDataRequest& req,
+                           MockProducerEndpoint::CommitDataCallback callback) {
+        ASSERT_EQ(2, req.chunks_to_move_size());
+        for (size_t i = 0; i < 2; i++) {
+          EXPECT_EQ(0u, req.chunks_to_move()[i].target_buffer());
+          SharedMemoryABI::Chunk chunk = shmem_abi->TryAcquireChunkForReading(
+              req.chunks_to_move()[i].page(), req.chunks_to_move()[i].chunk());
+          shmem_abi->ReleaseChunkAsFree(std::move(chunk));
+        }
+        callback();
+      }));
+
+  arbiter_->AbortStartupTracingForReservation(kTargetBufferReservationId2);
+  EXPECT_TRUE(IsArbiterFullyBound());
+  EXPECT_TRUE(flush_completed);
+
+  // SMB should be free again, as no writer holds on to any chunk anymore.
+  for (size_t i = 0; i < shmem_abi->num_pages(); i++)
+    EXPECT_TRUE(shmem_abi->is_page_free(i));
+}
+
 // TODO(primiano): add multi-threaded tests.
 
-}  // namespace
 }  // namespace perfetto
diff --git a/src/tracing/core/startup_trace_writer.cc b/src/tracing/core/startup_trace_writer.cc
deleted file mode 100644
index 26b8605..0000000
--- a/src/tracing/core/startup_trace_writer.cc
+++ /dev/null
@@ -1,629 +0,0 @@
-/*
- * 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/ext/tracing/core/startup_trace_writer.h"
-
-#include <numeric>
-
-#include "perfetto/base/logging.h"
-#include "perfetto/base/task_runner.h"
-#include "perfetto/ext/base/metatrace.h"
-#include "perfetto/ext/tracing/core/startup_trace_writer_registry.h"
-#include "perfetto/protozero/proto_utils.h"
-#include "protos/perfetto/trace/trace_packet.pbzero.h"
-#include "src/tracing/core/null_trace_writer.h"
-#include "src/tracing/core/patch_list.h"
-#include "src/tracing/core/shared_memory_arbiter_impl.h"
-
-using PageHeader = perfetto::SharedMemoryABI::PageHeader;
-using ChunkHeader = perfetto::SharedMemoryABI::ChunkHeader;
-
-namespace perfetto {
-
-namespace {
-
-static constexpr ChunkID kFirstChunkId = 0;
-
-SharedMemoryABI::Chunk NewChunk(SharedMemoryArbiterImpl* arbiter,
-                                WriterID writer_id,
-                                ChunkID chunk_id,
-                                bool fragmenting_packet,
-                                BufferExhaustedPolicy buffer_exhausted_policy) {
-  ChunkHeader::Packets packets = {};
-  if (fragmenting_packet) {
-    packets.count = 1;
-    packets.flags = ChunkHeader::kFirstPacketContinuesFromPrevChunk;
-  }
-
-  // The memory order of the stores below doesn't really matter. This |header|
-  // is just a local temporary object. The GetNewChunk() call below will copy it
-  // into the shared buffer with the proper barriers.
-  ChunkHeader header = {};
-  header.writer_id.store(writer_id, std::memory_order_relaxed);
-  header.chunk_id.store(chunk_id, std::memory_order_relaxed);
-  header.packets.store(packets, std::memory_order_relaxed);
-
-  return arbiter->GetNewChunk(header, buffer_exhausted_policy);
-}
-
-class LocalBufferReader {
- public:
-  LocalBufferReader(std::unique_ptr<protozero::ScatteredHeapBuffer> buffer)
-      : buffer_(std::move(buffer)),
-        buffer_slices_(buffer_->slices()),
-        cur_slice_(buffer_slices_.begin()) {}
-
-  size_t ReadBytes(SharedMemoryABI::Chunk* target_chunk,
-                   size_t num_bytes,
-                   size_t cur_payload_size) {
-    PERFETTO_CHECK(target_chunk->payload_size() >=
-                   num_bytes + cur_payload_size);
-    uint8_t* target_ptr = target_chunk->payload_begin() + cur_payload_size;
-    size_t bytes_read = 0;
-    while (bytes_read < num_bytes) {
-      if (cur_slice_ == buffer_slices_.end())
-        return bytes_read;
-
-      auto cur_slice_range = cur_slice_->GetUsedRange();
-
-      if (cur_slice_range.size() == cur_slice_offset_) {
-        cur_slice_offset_ = 0;
-        cur_slice_++;
-        continue;
-      }
-
-      size_t read_size = std::min(num_bytes - bytes_read,
-                                  cur_slice_range.size() - cur_slice_offset_);
-      memcpy(target_ptr + bytes_read, cur_slice_range.begin + cur_slice_offset_,
-             read_size);
-      cur_slice_offset_ += read_size;
-      bytes_read += read_size;
-
-      // Should have either read all of the chunk or completed reading now.
-      PERFETTO_DCHECK(cur_slice_offset_ == cur_slice_range.size() ||
-                      bytes_read == num_bytes);
-    }
-    return bytes_read;
-  }
-
-  size_t TotalUsedSize() const {
-    size_t used_size = 0;
-    for (const auto& slice : buffer_slices_) {
-      used_size += slice.GetUsedRange().size();
-    }
-    return used_size;
-  }
-
-  bool DidReadAllData() const {
-    if (cur_slice_ == buffer_slices_.end())
-      return true;
-
-    const auto next_slice = cur_slice_ + 1;
-    return next_slice == buffer_slices_.end() &&
-           cur_slice_->GetUsedRange().size() == cur_slice_offset_;
-  }
-
- private:
-  std::unique_ptr<protozero::ScatteredHeapBuffer> buffer_;
-  const std::vector<protozero::ScatteredHeapBuffer::Slice>& buffer_slices_;
-
-  // Iterator pointing to slice in |buffer_slices_| that we're currently reading
-  // from.
-  std::vector<protozero::ScatteredHeapBuffer::Slice>::const_iterator cur_slice_;
-  // Read offset in the current slice in bytes.
-  size_t cur_slice_offset_ = 0;
-};
-
-// Helper class that takes ownership of a LocalBufferReader its buffer and
-// commits the buffer's data into the assigned SMB in batches. After writing
-// each batch of data, it waits for the service to acknowledge the batch's
-// commit before continuing with the remaining data.
-class LocalBufferCommitter {
- public:
-  LocalBufferCommitter(std::unique_ptr<LocalBufferReader> local_buffer_reader,
-                       std::unique_ptr<std::vector<uint32_t>> packet_sizes,
-                       base::WeakPtr<SharedMemoryArbiterImpl> arbiter,
-                       WriterID writer_id,
-                       BufferID target_buffer,
-                       size_t chunks_per_batch,
-                       BufferExhaustedPolicy buffer_exhausted_policy,
-                       SharedMemoryABI::Chunk first_chunk)
-      : local_buffer_reader_(std::move(local_buffer_reader)),
-        packet_sizes_(std::move(packet_sizes)),
-        arbiter_(arbiter),
-        // TODO(eseckler): This assumes a fixed page layout of one chunk per
-        // page. If we ever end up supporting dynamic page layouts, we'd have to
-        // make sure that the arbiter gives us full-page chunks.
-        max_payload_size_(arbiter->page_size() - sizeof(PageHeader) -
-                          sizeof(ChunkHeader)),
-        writer_id_(writer_id),
-        target_buffer_(target_buffer),
-        chunks_per_batch_(chunks_per_batch),
-        buffer_exhausted_policy_(buffer_exhausted_policy),
-        cur_chunk_(std::move(first_chunk)) {
-    PERFETTO_DCHECK(cur_chunk_.is_valid());
-    PERFETTO_DCHECK(!packet_sizes_->empty());
-    remaining_packet_size_ = (*packet_sizes_)[packet_idx_];
-  }
-
-  static void CommitRemainingDataInBatches(
-      std::unique_ptr<LocalBufferCommitter> committer) {
-    // Give up and destroy the committer if the arbiter went away.
-    if (!committer->arbiter_)
-      return;
-
-    committer->CommitNextBatch();
-    if (committer->HasMoreDataToCommit()) {
-      // Flush the commit request to the service and wait for its response
-      // before continuing with the next batch.
-      std::shared_ptr<std::unique_ptr<LocalBufferCommitter>> committer_shared(
-          new std::unique_ptr<LocalBufferCommitter>(std::move(committer)));
-
-      (*committer_shared)
-          ->arbiter_->FlushPendingCommitDataRequests([committer_shared]() {
-            std::unique_ptr<LocalBufferCommitter> owned_committer(
-                committer_shared->release());
-            CommitRemainingDataInBatches(std::move(owned_committer));
-          });
-      return;
-    }
-
-    // We should have read all data from the local buffer.
-    PERFETTO_DCHECK(committer->local_buffer_reader_->DidReadAllData());
-    // Last chunk should have completed the last packet.
-    PERFETTO_DCHECK(!committer->fragmenting_packet_);
-
-    committer->arbiter_->FlushPendingCommitDataRequests();
-  }
-
-  size_t GetTotalNumChunksRequired() {
-    // We will write at least one chunk.
-    size_t num_chunks = 1;
-
-    size_t cur_payload_size = 0;
-    uint16_t cur_num_packets = 0;
-    for (size_t packet_idx = 0; packet_idx < packet_sizes_->size();
-         packet_idx++) {
-      uint32_t remaining_packet_size = (*packet_sizes_)[packet_idx];
-      ++cur_num_packets;
-      do {
-        uint32_t fragment_size = static_cast<uint32_t>(
-            std::min(static_cast<size_t>(remaining_packet_size),
-                     max_payload_size_ - cur_payload_size -
-                         SharedMemoryABI::kPacketHeaderSize));
-        cur_payload_size += SharedMemoryABI::kPacketHeaderSize;
-        cur_payload_size += fragment_size;
-        remaining_packet_size -= fragment_size;
-
-        // We need another chunk if we've filled its payload (i.e., cannot fit
-        // another packet's header) or reached the maximum number of packets.
-        bool next_chunk =
-            cur_payload_size >=
-                max_payload_size_ - SharedMemoryABI::kPacketHeaderSize ||
-            cur_num_packets == ChunkHeader::Packets::kMaxCount;
-
-        if (next_chunk) {
-          num_chunks++;
-          bool is_fragmenting = remaining_packet_size > 0;
-          cur_num_packets = is_fragmenting ? 1 : 0;
-          cur_payload_size = 0;
-        }
-      } while (remaining_packet_size > 0);
-    }
-
-    return num_chunks;
-  }
-
- private:
-  bool HasMoreDataToCommit() const {
-    PERFETTO_DCHECK(packet_idx_ <= packet_sizes_->size());
-    return packet_idx_ < packet_sizes_->size() || remaining_packet_size_ != 0;
-  }
-
-  // Reads (part of) the remaining data from |local_buffer_reader_| and writes
-  // the next batch of chunks into the SMB.
-  void CommitNextBatch() {
-    PERFETTO_METATRACE_SCOPED(TAG_TRACE_WRITER,
-                              TRACE_WRITER_COMMIT_STARTUP_WRITER_BATCH);
-    for (size_t num_chunks = 0;
-         (!chunks_per_batch_ || num_chunks < chunks_per_batch_) &&
-         HasMoreDataToCommit();
-         num_chunks++) {
-      if (!CommitNextChunk()) {
-        // We ran out of SMB space. Send the current batch early and retry later
-        // with the next batch.
-        break;
-      }
-    }
-  }
-
-  bool CommitNextChunk() {
-    PERFETTO_DCHECK(HasMoreDataToCommit());
-
-    // First chunk is acquired before LocalBufferCommitter is created, so we may
-    // already have a valid chunk.
-    if (!cur_chunk_.is_valid()) {
-      cur_chunk_ = NewChunk(arbiter_.get(), writer_id_, next_chunk_id_,
-                            fragmenting_packet_, buffer_exhausted_policy_);
-
-      if (!cur_chunk_.is_valid())
-        return false;
-
-      next_chunk_id_++;
-    }
-
-    // See comment at initialization of |max_payload_size_|.
-    PERFETTO_CHECK(max_payload_size_ == cur_chunk_.payload_size());
-
-    // Iterate over remaining packets, starting at |packet_idx_|. Write as much
-    // data as possible into |chunk| while not exceeding the chunk's payload
-    // size and the maximum number of packets per chunk.
-    size_t cur_payload_size = 0;
-    uint16_t cur_num_packets = 0;
-    PatchList empty_patch_list;
-    PERFETTO_DCHECK(packet_idx_ < packet_sizes_->size());
-    PERFETTO_DCHECK((*packet_sizes_)[packet_idx_] >= remaining_packet_size_ &&
-                    (remaining_packet_size_ || !(*packet_sizes_)[packet_idx_]));
-    while (HasMoreDataToCommit()) {
-      ++cur_num_packets;
-
-      // The packet may not fit completely into the chunk.
-      uint32_t fragment_size = static_cast<uint32_t>(
-          std::min(static_cast<size_t>(remaining_packet_size_),
-                   max_payload_size_ - cur_payload_size -
-                       SharedMemoryABI::kPacketHeaderSize));
-
-      // Write packet header, i.e. the fragment size.
-      protozero::proto_utils::WriteRedundantVarInt(
-          fragment_size, cur_chunk_.payload_begin() + cur_payload_size);
-      cur_payload_size += SharedMemoryABI::kPacketHeaderSize;
-
-      // Copy packet content into the chunk.
-      size_t bytes_read = local_buffer_reader_->ReadBytes(
-          &cur_chunk_, fragment_size, cur_payload_size);
-      PERFETTO_DCHECK(bytes_read == fragment_size);
-
-      cur_payload_size += fragment_size;
-      remaining_packet_size_ -= fragment_size;
-
-      fragmenting_packet_ = remaining_packet_size_ > 0;
-      if (!fragmenting_packet_) {
-        ++packet_idx_;
-        if (packet_idx_ < packet_sizes_->size()) {
-          remaining_packet_size_ = (*packet_sizes_)[packet_idx_];
-        }
-      }
-
-      // We should return the current chunk if we've filled its payload, reached
-      // the maximum number of packets, or wrote everything we wanted to.
-      bool return_chunk =
-          cur_payload_size >=
-              max_payload_size_ - SharedMemoryABI::kPacketHeaderSize ||
-          cur_num_packets == ChunkHeader::Packets::kMaxCount ||
-          !HasMoreDataToCommit();
-
-      if (return_chunk)
-        break;
-    }
-
-    auto new_packet_count = cur_chunk_.IncreasePacketCountTo(cur_num_packets);
-    PERFETTO_DCHECK(new_packet_count == cur_num_packets);
-
-    if (fragmenting_packet_) {
-      PERFETTO_DCHECK(cur_payload_size == max_payload_size_);
-      cur_chunk_.SetFlag(ChunkHeader::kLastPacketContinuesOnNextChunk);
-    }
-
-    arbiter_->ReturnCompletedChunk(std::move(cur_chunk_), target_buffer_,
-                                   &empty_patch_list);
-    return true;
-  }
-
-  std::unique_ptr<LocalBufferReader> local_buffer_reader_;
-  std::unique_ptr<std::vector<uint32_t>> packet_sizes_;
-  base::WeakPtr<SharedMemoryArbiterImpl> arbiter_;
-  const size_t max_payload_size_;
-  const WriterID writer_id_;
-  const BufferID target_buffer_;
-  const size_t chunks_per_batch_;
-  BufferExhaustedPolicy buffer_exhausted_policy_;
-  SharedMemoryABI::Chunk cur_chunk_;
-  // We receive the first chunk in the constructor, thus the next chunk will be
-  // the second one.
-  ChunkID next_chunk_id_ = kFirstChunkId + 1;
-  size_t packet_idx_ = 0;
-  uint32_t remaining_packet_size_ = 0;
-  bool fragmenting_packet_ = false;
-};
-
-}  // namespace
-
-StartupTraceWriter::StartupTraceWriter(
-    std::shared_ptr<StartupTraceWriterRegistryHandle> registry_handle,
-    BufferExhaustedPolicy buffer_exhausted_policy,
-    size_t max_buffer_size_bytes)
-    : registry_handle_(std::move(registry_handle)),
-      buffer_exhausted_policy_(buffer_exhausted_policy),
-      max_buffer_size_bytes_(max_buffer_size_bytes),
-      memory_buffer_(new protozero::ScatteredHeapBuffer()),
-      memory_stream_writer_(
-          new protozero::ScatteredStreamWriter(memory_buffer_.get())),
-      packet_sizes_(new std::vector<uint32_t>()) {
-  memory_buffer_->set_writer(memory_stream_writer_.get());
-  PERFETTO_DETACH_FROM_THREAD(writer_thread_checker_);
-}
-
-StartupTraceWriter::StartupTraceWriter(
-    std::unique_ptr<TraceWriter> trace_writer)
-    : was_bound_(true), trace_writer_(std::move(trace_writer)) {}
-
-StartupTraceWriter::~StartupTraceWriter() {
-  // Should have been returned to the registry before destruction.
-  PERFETTO_DCHECK(!registry_handle_);
-}
-
-// static
-void StartupTraceWriter::ReturnToRegistry(
-    std::unique_ptr<StartupTraceWriter> writer) {
-  auto registry_handle = std::move(writer->registry_handle_);
-  if (registry_handle) {
-    // May destroy |writer|.
-    registry_handle->ReturnWriterToRegistry(std::move(writer));
-  }
-}
-
-bool StartupTraceWriter::BindToArbiter(SharedMemoryArbiterImpl* arbiter,
-                                       BufferID target_buffer,
-                                       size_t chunks_per_batch) {
-  // LocalBufferCommitter requires a WeakPtr to the arbiter, and thus needs to
-  // execute on the arbiter's task runner.
-  PERFETTO_DCHECK(arbiter->task_runner()->RunsTasksOnCurrentThread());
-
-  // Create and destroy trace writer without holding lock, since this will post
-  // a task and task posting may trigger a trace event, which would cause a
-  // deadlock. This may create a few more trace writers than necessary in cases
-  // where a concurrent write is in progress (other than causing some
-  // computational overhead, this is not problematic).
-  auto trace_writer =
-      arbiter->CreateTraceWriter(target_buffer, buffer_exhausted_policy_);
-
-  {
-    std::lock_guard<std::mutex> lock(lock_);
-
-    PERFETTO_DCHECK(!trace_writer_);
-
-    // Can't bind while the writer thread is writing.
-    if (write_in_progress_)
-      return false;
-
-    // If there's a pending trace packet, it should have been completed by the
-    // writer thread before write_in_progress_ is reset.
-    if (cur_packet_) {
-      PERFETTO_DCHECK(cur_packet_->is_finalized());
-      cur_packet_.reset();
-    }
-
-    // Successfully bind if we don't have any data or no valid trace writer.
-    if (packet_sizes_->empty() || !trace_writer->writer_id()) {
-      trace_writer_ = std::move(trace_writer);
-      memory_buffer_.reset();
-      packet_sizes_.reset();
-      memory_stream_writer_.reset();
-      return true;
-    }
-
-    // We need to ensure that we commit at least one chunk now, otherwise the
-    // service might receive and erroneously start reading from a future chunk
-    // committed by the underlying trace writer. Thus, we attempt to acquire the
-    // first chunk and bail out if we fail (we'll retry later).
-    SharedMemoryABI::Chunk first_chunk =
-        NewChunk(arbiter, trace_writer->writer_id(), kFirstChunkId,
-                 /*fragmenting_packet=*/false, buffer_exhausted_policy_);
-    if (!first_chunk.is_valid())
-      return false;
-
-    trace_writer_ = std::move(trace_writer);
-    ChunkID next_chunk_id = CommitLocalBufferChunks(
-        arbiter, trace_writer_->writer_id(), target_buffer, chunks_per_batch,
-        std::move(first_chunk));
-
-    // The real TraceWriter should start writing at the subsequent chunk ID.
-    bool success = trace_writer_->SetFirstChunkId(next_chunk_id);
-    PERFETTO_DCHECK(success);
-  }
-
-  return true;
-}
-
-TraceWriter::TracePacketHandle StartupTraceWriter::NewTracePacket() {
-  PERFETTO_DCHECK_THREAD(writer_thread_checker_);
-
-  // Check if we are already bound without grabbing the lock. This is an
-  // optimization to avoid any locking in the common case where the proxy was
-  // bound some time ago.
-  if (PERFETTO_LIKELY(was_bound_)) {
-    PERFETTO_DCHECK(!cur_packet_);
-    PERFETTO_DCHECK(trace_writer_);
-    return trace_writer_->NewTracePacket();
-  }
-
-  // Now grab the lock and safely check whether we are still unbound.
-  {
-    std::unique_lock<std::mutex> lock(lock_);
-    if (trace_writer_) {
-      PERFETTO_DCHECK(!cur_packet_);
-      // Set the |was_bound_| flag to avoid locking in future calls to
-      // NewTracePacket().
-      was_bound_ = true;
-      // Don't hold the lock while calling NewTracePacket() on |trace_writer_|.
-      // This is safe because |trace_writer_| remains valid once set. It also
-      // avoids deadlocks that may be caused by holding the lock while waiting
-      // for a new SMB chunk in |trace_writer_|.
-      lock.unlock();
-      return trace_writer_->NewTracePacket();
-    }
-
-    // Check if we already exceeded the maximum size of the local buffer, and if
-    // so, write into nowhere.
-    if (null_trace_writer_ ||
-        memory_buffer_->GetTotalSize() >= max_buffer_size_bytes_) {
-      if (!null_trace_writer_) {
-        null_trace_writer_.reset(new NullTraceWriter());
-
-        // Write a packet that marks data loss.
-        std::unique_ptr<protos::pbzero::TracePacket> packet(
-            new protos::pbzero::TracePacket());
-        packet->Reset(memory_stream_writer_.get());
-        {
-          TraceWriter::TracePacketHandle handle(packet.get());
-          handle->set_previous_packet_dropped(true);
-        }
-        uint32_t packet_size = packet->Finalize();
-        packet_sizes_->push_back(packet_size);
-      }
-      return null_trace_writer_->NewTracePacket();
-    }
-
-    // Not bound. Make sure it stays this way until the TracePacketHandle goes
-    // out of scope by setting |write_in_progress_|.
-    PERFETTO_DCHECK(!write_in_progress_);
-    write_in_progress_ = true;
-  }
-
-  // Write to the local buffer.
-  if (cur_packet_) {
-    // If we hit this, the caller is calling NewTracePacket() without having
-    // finalized the previous packet.
-    PERFETTO_DCHECK(cur_packet_->is_finalized());
-  } else {
-    cur_packet_.reset(new protos::pbzero::TracePacket());
-  }
-
-  cur_packet_->Reset(memory_stream_writer_.get());
-  TraceWriter::TracePacketHandle handle(cur_packet_.get());
-  // |this| outlives the packet handle.
-  handle.set_finalization_listener(this);
-  return handle;
-}
-
-void StartupTraceWriter::Flush(std::function<void()> callback) {
-  PERFETTO_DCHECK_THREAD(writer_thread_checker_);
-  // It's fine to check |was_bound_| instead of acquiring the lock because
-  // |trace_writer_| will only need flushing after the first trace packet was
-  // written to it and |was_bound_| is set.
-  if (PERFETTO_LIKELY(was_bound_)) {
-    PERFETTO_DCHECK(trace_writer_);
-    return trace_writer_->Flush(std::move(callback));
-  }
-
-  // Can't flush while unbound.
-  if (callback)
-    callback();
-}
-
-WriterID StartupTraceWriter::writer_id() const {
-  PERFETTO_DCHECK_THREAD(writer_thread_checker_);
-  // We can't acquire the lock because this is a const method. So we'll only
-  // proxy to |trace_writer_| once we have written the first packet to it
-  // instead.
-  if (PERFETTO_LIKELY(was_bound_)) {
-    PERFETTO_DCHECK(trace_writer_);
-    return trace_writer_->writer_id();
-  }
-  return 0;
-}
-
-uint64_t StartupTraceWriter::written() const {
-  PERFETTO_DCHECK_THREAD(writer_thread_checker_);
-  // We can't acquire the lock because this is a const method. So we'll only
-  // proxy to |trace_writer_| once we have written the first packet to it
-  // instead.
-  if (PERFETTO_LIKELY(was_bound_)) {
-    PERFETTO_DCHECK(trace_writer_);
-    return trace_writer_->written();
-  }
-  return 0;
-}
-
-size_t StartupTraceWriter::used_buffer_size() {
-  PERFETTO_DCHECK_THREAD(writer_thread_checker_);
-  if (PERFETTO_LIKELY(was_bound_))
-    return 0;
-
-  std::lock_guard<std::mutex> lock(lock_);
-  if (trace_writer_)
-    return 0;
-
-  size_t used_size = 0;
-  memory_buffer_->AdjustUsedSizeOfCurrentSlice();
-  for (const auto& slice : memory_buffer_->slices()) {
-    used_size += slice.GetUsedRange().size();
-  }
-  return used_size;
-}
-
-void StartupTraceWriter::OnMessageFinalized(protozero::Message* message) {
-  PERFETTO_DCHECK(cur_packet_.get() == message);
-  PERFETTO_DCHECK(cur_packet_->is_finalized());
-  // Finalize() is a no-op because the packet is already finalized.
-  uint32_t packet_size = cur_packet_->Finalize();
-  packet_sizes_->push_back(packet_size);
-
-  // Write is complete, reset the flag to allow binding.
-  std::lock_guard<std::mutex> lock(lock_);
-  PERFETTO_DCHECK(write_in_progress_);
-  write_in_progress_ = false;
-}
-
-ChunkID StartupTraceWriter::CommitLocalBufferChunks(
-    SharedMemoryArbiterImpl* arbiter,
-    WriterID writer_id,
-    BufferID target_buffer,
-    size_t chunks_per_batch,
-    SharedMemoryABI::Chunk first_chunk) {
-  PERFETTO_DCHECK(!packet_sizes_->empty());
-  PERFETTO_DCHECK(writer_id);
-
-  memory_buffer_->AdjustUsedSizeOfCurrentSlice();
-  memory_stream_writer_.reset();
-
-  std::unique_ptr<LocalBufferReader> local_buffer_reader(
-      new LocalBufferReader(std::move(memory_buffer_)));
-
-  PERFETTO_DCHECK(local_buffer_reader->TotalUsedSize() ==
-                  std::accumulate(packet_sizes_->begin(), packet_sizes_->end(),
-                                  static_cast<size_t>(0u)));
-
-  std::unique_ptr<LocalBufferCommitter> committer(new LocalBufferCommitter(
-      std::move(local_buffer_reader), std::move(packet_sizes_),
-      arbiter->GetWeakPtr(), writer_id, target_buffer, chunks_per_batch,
-      buffer_exhausted_policy_, std::move(first_chunk)));
-
-  ChunkID next_chunk_id =
-      kFirstChunkId +
-      static_cast<ChunkID>(committer->GetTotalNumChunksRequired());
-
-  // Write the chunks to the SMB in smaller batches to avoid large bursts that
-  // could fill up the SMB completely and lead to stalls or data loss. We'll
-  // continue writing the chunks asynchronously. We need to ensure that we write
-  // at least one chunk now, otherwise the service might receive and erroneously
-  // start reading from a future chunk committed by the underlying trace writer.
-  LocalBufferCommitter::CommitRemainingDataInBatches(std::move(committer));
-
-  return next_chunk_id;
-}
-
-}  // namespace perfetto
diff --git a/src/tracing/core/startup_trace_writer_registry.cc b/src/tracing/core/startup_trace_writer_registry.cc
deleted file mode 100644
index e576be8..0000000
--- a/src/tracing/core/startup_trace_writer_registry.cc
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * 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/ext/tracing/core/startup_trace_writer_registry.h"
-
-#include <algorithm>
-#include <cmath>
-#include <functional>
-
-#include "perfetto/base/logging.h"
-#include "perfetto/base/task_runner.h"
-#include "perfetto/ext/tracing/core/startup_trace_writer.h"
-#include "src/tracing/core/shared_memory_arbiter_impl.h"
-
-using ChunkHeader = perfetto::SharedMemoryABI::ChunkHeader;
-
-namespace perfetto {
-
-StartupTraceWriterRegistryHandle::StartupTraceWriterRegistryHandle(
-    StartupTraceWriterRegistry* registry)
-    : registry_(registry) {}
-
-void StartupTraceWriterRegistryHandle::ReturnWriterToRegistry(
-    std::unique_ptr<StartupTraceWriter> writer) {
-  std::lock_guard<std::mutex> lock(lock_);
-  if (registry_)
-    registry_->ReturnTraceWriter(std::move(writer));
-}
-
-void StartupTraceWriterRegistryHandle::OnRegistryDestroyed() {
-  std::lock_guard<std::mutex> lock(lock_);
-  registry_ = nullptr;
-}
-
-StartupTraceWriterRegistry::StartupTraceWriterRegistry()
-    : handle_(std::make_shared<StartupTraceWriterRegistryHandle>(this)) {}
-
-StartupTraceWriterRegistry::~StartupTraceWriterRegistry() {
-  handle_->OnRegistryDestroyed();
-}
-
-// static
-constexpr size_t StartupTraceWriterRegistry::kDefaultMaxBufferSizeBytes;
-
-std::unique_ptr<StartupTraceWriter>
-StartupTraceWriterRegistry::CreateUnboundTraceWriter(
-    BufferExhaustedPolicy buffer_exhausted_policy,
-    size_t max_buffer_size_bytes) {
-  std::lock_guard<std::mutex> lock(lock_);
-  PERFETTO_DCHECK(!arbiter_);  // Should only be called while unbound.
-  std::unique_ptr<StartupTraceWriter> writer(new StartupTraceWriter(
-      handle_, buffer_exhausted_policy, max_buffer_size_bytes));
-  unbound_writers_.push_back(writer.get());
-  return writer;
-}
-
-void StartupTraceWriterRegistry::ReturnTraceWriter(
-    std::unique_ptr<StartupTraceWriter> trace_writer) {
-  std::lock_guard<std::mutex> lock(lock_);
-  PERFETTO_DCHECK(!trace_writer->write_in_progress_);
-  auto it = std::find(unbound_writers_.begin(), unbound_writers_.end(),
-                      trace_writer.get());
-
-  // If the registry is already bound, but the writer wasn't, bind it now.
-  if (arbiter_) {
-    if (it == unbound_writers_.end()) {
-      // Nothing to do, the writer was already bound.
-      return;
-    }
-
-    // This should succeed since nobody can write to this writer concurrently.
-    bool success = trace_writer->BindToArbiter(arbiter_, target_buffer_,
-                                               chunks_per_batch_);
-    PERFETTO_DCHECK(success);
-    unbound_writers_.erase(it);
-
-    OnUnboundWritersRemovedLocked();
-    return;
-  }
-
-  // If the registry was not bound yet, keep the writer alive until it is.
-  PERFETTO_DCHECK(it != unbound_writers_.end());
-  unbound_writers_.erase(it);
-  unbound_owned_writers_.push_back(std::move(trace_writer));
-}
-
-void StartupTraceWriterRegistry::BindToArbiter(
-    SharedMemoryArbiterImpl* arbiter,
-    BufferID target_buffer,
-    base::TaskRunner* task_runner,
-    std::function<void(StartupTraceWriterRegistry*)> on_bound_callback) {
-  std::vector<std::unique_ptr<StartupTraceWriter>> unbound_owned_writers;
-  {
-    std::lock_guard<std::mutex> lock(lock_);
-    PERFETTO_DCHECK(!arbiter_);
-    arbiter_ = arbiter;
-    target_buffer_ = target_buffer;
-    task_runner_ = task_runner;
-
-    // Attempt to use at most half the SMB for binding of StartupTraceWriters at
-    // the same time. In the worst case, all writers are binding at the same
-    // time, so divide it up between them.
-    //
-    // TODO(eseckler): This assumes that there's only a single registry at the
-    // same time. SharedMemoryArbiterImpl should advise us how much of the SMB
-    // we're allowed to use in the first place.
-    size_t num_writers =
-        unbound_owned_writers_.size() + unbound_writers_.size();
-    if (num_writers) {
-      chunks_per_batch_ = arbiter_->num_pages() / 2 / num_writers;
-    } else {
-      chunks_per_batch_ = arbiter_->num_pages() / 2;
-    }
-    // We should use at least one chunk per batch.
-    chunks_per_batch_ = std::max(chunks_per_batch_, static_cast<size_t>(1u));
-
-    // Weakptrs should be valid on |task_runner|. For this, the factory needs to
-    // be created on |task_runner|, i.e. BindToArbiter must be called on
-    // |task_runner|.
-    PERFETTO_DCHECK(task_runner_->RunsTasksOnCurrentThread());
-    weak_ptr_factory_.reset(
-        new base::WeakPtrFactory<StartupTraceWriterRegistry>(this));
-    on_bound_callback_ = std::move(on_bound_callback);
-    // We can't destroy the writers while holding |lock_|, so we swap them out
-    // here instead. After we are bound, no more writers can be added to the
-    // list.
-    unbound_owned_writers.swap(unbound_owned_writers_);
-  }
-
-  // Bind and destroy the owned writers.
-  for (const auto& writer : unbound_owned_writers) {
-    // This should succeed since nobody can write to these writers concurrently.
-    bool success =
-        writer->BindToArbiter(arbiter_, target_buffer_, chunks_per_batch_);
-    PERFETTO_DCHECK(success);
-  }
-  unbound_owned_writers.clear();
-
-  TryBindWriters();
-}
-
-void StartupTraceWriterRegistry::TryBindWriters() {
-  std::lock_guard<std::mutex> lock(lock_);
-  for (auto it = unbound_writers_.begin(); it != unbound_writers_.end();) {
-    if ((*it)->BindToArbiter(arbiter_, target_buffer_, chunks_per_batch_)) {
-      it = unbound_writers_.erase(it);
-    } else {
-      break;
-    }
-  }
-  if (!unbound_writers_.empty()) {
-    auto weak_this = weak_ptr_factory_->GetWeakPtr();
-    task_runner_->PostTask([weak_this] {
-      if (weak_this)
-        weak_this->TryBindWriters();
-    });
-  }
-  OnUnboundWritersRemovedLocked();
-}
-
-void StartupTraceWriterRegistry::OnUnboundWritersRemovedLocked() {
-  if (!unbound_writers_.empty() || !task_runner_ || !on_bound_callback_)
-    return;
-
-  PERFETTO_DCHECK(weak_ptr_factory_);
-  auto weak_this = weak_ptr_factory_->GetWeakPtr();
-  // Run callback in PostTask() since the callback may delete |this| and thus
-  // might otherwise cause a deadlock.
-  auto callback = on_bound_callback_;
-  on_bound_callback_ = nullptr;
-  task_runner_->PostTask([weak_this, callback]() {
-    if (!weak_this)
-      return;
-    // Note: callback may delete |this|.
-    callback(weak_this.get());
-  });
-}
-
-}  // namespace perfetto
diff --git a/src/tracing/core/startup_trace_writer_unittest.cc b/src/tracing/core/startup_trace_writer_unittest.cc
deleted file mode 100644
index 3942124..0000000
--- a/src/tracing/core/startup_trace_writer_unittest.cc
+++ /dev/null
@@ -1,509 +0,0 @@
-/*
- * 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/ext/tracing/core/startup_trace_writer.h"
-
-#include "perfetto/ext/tracing/core/startup_trace_writer_registry.h"
-#include "perfetto/ext/tracing/core/trace_packet.h"
-#include "perfetto/ext/tracing/core/tracing_service.h"
-#include "src/base/test/gtest_test_suite.h"
-#include "src/base/test/test_task_runner.h"
-#include "src/tracing/core/patch_list.h"
-#include "src/tracing/core/shared_memory_arbiter_impl.h"
-#include "src/tracing/core/trace_buffer.h"
-#include "src/tracing/test/aligned_buffer_test.h"
-#include "src/tracing/test/fake_producer_endpoint.h"
-#include "test/gtest_and_gmock.h"
-
-#include "protos/perfetto/trace/test_event.gen.h"
-#include "protos/perfetto/trace/test_event.pbzero.h"
-#include "protos/perfetto/trace/trace_packet.gen.h"
-#include "protos/perfetto/trace/trace_packet.pbzero.h"
-
-namespace perfetto {
-
-class StartupTraceWriterTest : public AlignedBufferTest {
- public:
-  void SetUp() override {
-    SharedMemoryArbiterImpl::set_default_layout_for_testing(
-        SharedMemoryABI::PageLayout::kPageDiv1);
-    AlignedBufferTest::SetUp();
-    task_runner_.reset(new base::TestTaskRunner());
-    arbiter_.reset(new SharedMemoryArbiterImpl(buf(), buf_size(), page_size(),
-                                               &fake_producer_endpoint_,
-                                               task_runner_.get()));
-  }
-
-  void TearDown() override {
-    arbiter_.reset();
-    task_runner_.reset();
-  }
-
-  std::unique_ptr<StartupTraceWriter> CreateUnboundWriter() {
-    std::shared_ptr<StartupTraceWriterRegistryHandle> registry;
-    return std::unique_ptr<StartupTraceWriter>(new StartupTraceWriter(
-        registry, BufferExhaustedPolicy::kDrop, max_buffer_size_bytes()));
-  }
-
-  bool BindWriter(StartupTraceWriter* writer, size_t chunks_per_batch = 0) {
-    const BufferID kBufId = 42;
-    return writer->BindToArbiter(arbiter_.get(), kBufId, chunks_per_batch);
-  }
-
-  void WritePackets(StartupTraceWriter* writer, size_t packet_count) {
-    for (size_t i = 0; i < packet_count; i++) {
-      auto packet = writer->NewTracePacket();
-      packet->set_for_testing()->set_str(kPacketPayload);
-    }
-  }
-
-  size_t CountCompleteChunksInSMB() {
-    SharedMemoryABI* abi = arbiter_->shmem_abi_for_testing();
-    size_t num_complete_chunks = 0;
-    for (size_t page_idx = 0; page_idx < kNumPages; page_idx++) {
-      uint32_t page_layout = abi->GetPageLayout(page_idx);
-      size_t num_chunks = SharedMemoryABI::GetNumChunksForLayout(page_layout);
-      for (size_t chunk_idx = 0; chunk_idx < num_chunks; chunk_idx++) {
-        auto chunk_state = abi->GetChunkState(page_idx, chunk_idx);
-        if (chunk_state == SharedMemoryABI::kChunkComplete)
-          num_complete_chunks++;
-      }
-    }
-    return num_complete_chunks;
-  }
-
-  void VerifyPackets(size_t expected_count,
-                     bool expect_data_loss_packet = false) {
-    SharedMemoryABI* abi = arbiter_->shmem_abi_for_testing();
-    auto buffer = TraceBuffer::Create(abi->size());
-
-    if (expect_data_loss_packet)
-      expected_count++;
-
-    size_t total_packets_count = 0;
-    ChunkID current_max_chunk_id = 0;
-    for (size_t page_idx = 0; page_idx < kNumPages; page_idx++) {
-      uint32_t page_layout = abi->GetPageLayout(page_idx);
-      size_t num_chunks = SharedMemoryABI::GetNumChunksForLayout(page_layout);
-      for (size_t chunk_idx = 0; chunk_idx < num_chunks; chunk_idx++) {
-        auto chunk_state = abi->GetChunkState(page_idx, chunk_idx);
-        ASSERT_TRUE(chunk_state == SharedMemoryABI::kChunkFree ||
-                    chunk_state == SharedMemoryABI::kChunkComplete);
-        auto chunk = abi->TryAcquireChunkForReading(page_idx, chunk_idx);
-        if (!chunk.is_valid())
-          continue;
-
-        // Should only see new chunks with IDs larger than the previous read
-        // since our reads and writes are serialized.
-        ChunkID chunk_id = chunk.header()->chunk_id.load();
-        if (last_read_max_chunk_id_ != 0) {
-          EXPECT_LT(last_read_max_chunk_id_, chunk_id);
-        }
-        current_max_chunk_id = std::max(current_max_chunk_id, chunk_id);
-
-        auto packets_header = chunk.header()->packets.load();
-        total_packets_count += packets_header.count;
-        if (packets_header.flags &
-            SharedMemoryABI::ChunkHeader::kFirstPacketContinuesFromPrevChunk) {
-          // Don't count fragmented packets twice.
-          total_packets_count--;
-        }
-
-        buffer->CopyChunkUntrusted(
-            /*producer_id_trusted=*/1, /*producer_uid_trusted=*/1,
-            chunk.header()->writer_id.load(), chunk_id, packets_header.count,
-            packets_header.flags, /*chunk_complete=*/true,
-            chunk.payload_begin(), chunk.payload_size());
-        abi->ReleaseChunkAsFree(std::move(chunk));
-      }
-    }
-    last_read_max_chunk_id_ = current_max_chunk_id;
-    EXPECT_EQ(expected_count, total_packets_count);
-
-    // Now verify chunk and packet contents.
-    buffer->BeginRead();
-    size_t num_packets_read = 0;
-    while (true) {
-      TracePacket packet;
-      TraceBuffer::PacketSequenceProperties sequence_properties{};
-      bool previous_packet_dropped;
-      if (!buffer->ReadNextTracePacket(&packet, &sequence_properties,
-                                       &previous_packet_dropped)) {
-        break;
-      }
-      EXPECT_EQ(static_cast<uid_t>(1),
-                sequence_properties.producer_uid_trusted);
-
-      protos::gen::TracePacket parsed_packet;
-      bool res = parsed_packet.ParseFromString(packet.GetRawBytesForTesting());
-      EXPECT_TRUE(res);
-      if (!res)
-        break;
-
-      // If the buffer size was exceeded, the data loss packet should be the
-      // last committed packet.
-      if (expect_data_loss_packet && parsed_packet.previous_packet_dropped()) {
-        num_packets_read++;
-        EXPECT_EQ(expected_count, num_packets_read);
-        break;
-      }
-
-      EXPECT_TRUE(parsed_packet.has_for_testing());
-      EXPECT_EQ(kPacketPayload, parsed_packet.for_testing().str());
-      num_packets_read++;
-    }
-    EXPECT_EQ(expected_count, num_packets_read);
-  }
-
-  size_t GetUnboundWriterCount(
-      const StartupTraceWriterRegistry& registry) const {
-    return registry.unbound_writers_.size() +
-           registry.unbound_owned_writers_.size();
-  }
-
-  size_t GetBindingRegistriesCount(
-      const SharedMemoryArbiterImpl& arbiter) const {
-    return arbiter.startup_trace_writer_registries_.size();
-  }
-
-  size_t GetUnboundWriterCount(const SharedMemoryArbiterImpl& arbiter) const {
-    size_t count = 0u;
-    for (const auto& reg : arbiter.startup_trace_writer_registries_) {
-      count += reg->unbound_writers_.size();
-      count += reg->unbound_owned_writers_.size();
-    }
-    return count;
-  }
-
-  size_t GetTotalBufferSize(StartupTraceWriter* writer) const {
-    return writer->memory_buffer_->GetTotalSize();
-  }
-
-  size_t max_buffer_size_bytes() const { return page_size() * 5; }
-
- protected:
-  static constexpr char kPacketPayload[] = "foo";
-
-  FakeProducerEndpoint fake_producer_endpoint_;
-  std::unique_ptr<base::TestTaskRunner> task_runner_;
-  std::unique_ptr<SharedMemoryArbiterImpl> arbiter_;
-  std::function<void(const std::vector<uint32_t>&)> on_pages_complete_;
-
-  ChunkID last_read_max_chunk_id_ = 0;
-};
-
-constexpr char StartupTraceWriterTest::kPacketPayload[];
-
-namespace {
-
-size_t const kPageSizes[] = {4096, 32768};
-INSTANTIATE_TEST_SUITE_P(PageSize,
-                         StartupTraceWriterTest,
-                         ::testing::ValuesIn(kPageSizes));
-
-TEST_P(StartupTraceWriterTest, CreateUnboundAndBind) {
-  auto writer = CreateUnboundWriter();
-
-  // Bind writer right away without having written any data before.
-  EXPECT_TRUE(BindWriter(writer.get()));
-
-  const size_t kNumPackets = 32;
-  WritePackets(writer.get(), kNumPackets);
-  // Finalizes the last packet and returns the chunk.
-  writer.reset();
-
-  VerifyPackets(kNumPackets);
-}
-
-TEST_P(StartupTraceWriterTest, CreateBound) {
-  // Create a bound writer immediately.
-  const BufferID kBufId = 42;
-  std::unique_ptr<StartupTraceWriter> writer(
-      new StartupTraceWriter(arbiter_->CreateTraceWriter(kBufId)));
-
-  const size_t kNumPackets = 32;
-  WritePackets(writer.get(), kNumPackets);
-  // Finalizes the last packet and returns the chunk.
-  writer.reset();
-
-  VerifyPackets(kNumPackets);
-}
-
-TEST_P(StartupTraceWriterTest, WriteWhileUnboundAndDiscard) {
-  auto writer = CreateUnboundWriter();
-
-  const size_t kNumPackets = 32;
-  WritePackets(writer.get(), kNumPackets);
-
-  // Should discard the written data.
-  writer.reset();
-
-  VerifyPackets(0);
-}
-
-TEST_P(StartupTraceWriterTest, WriteWhileUnboundAndBind) {
-  auto writer = CreateUnboundWriter();
-
-  const size_t kNumPackets = 32;
-  WritePackets(writer.get(), kNumPackets);
-
-  // Binding the writer should cause the previously written packets to be
-  // written to the SMB and committed.
-  EXPECT_TRUE(BindWriter(writer.get()));
-
-  VerifyPackets(kNumPackets);
-
-  // Any further packets should be written to the SMB directly.
-  const size_t kNumAdditionalPackets = 16;
-  WritePackets(writer.get(), kNumAdditionalPackets);
-  // Finalizes the last packet and returns the chunk.
-  writer.reset();
-
-  VerifyPackets(kNumAdditionalPackets);
-}
-
-TEST_P(StartupTraceWriterTest, WriteMultipleChunksWhileUnboundAndBind) {
-  auto writer = CreateUnboundWriter();
-
-  // Write a single packet to determine its size in the buffer.
-  WritePackets(writer.get(), 1);
-  size_t packet_size = writer->used_buffer_size();
-
-  // Write at least 3 pages worth of packets.
-  const size_t kNumPackets = (page_size() * 3 + packet_size - 1) / packet_size;
-  WritePackets(writer.get(), kNumPackets);
-
-  // Binding the writer should cause the previously written packets to be
-  // written to the SMB and committed.
-  EXPECT_TRUE(BindWriter(writer.get()));
-
-  VerifyPackets(kNumPackets + 1);
-
-  // Any further packets should be written to the SMB directly.
-  const size_t kNumAdditionalPackets = 16;
-  WritePackets(writer.get(), kNumAdditionalPackets);
-  // Finalizes the last packet and returns the chunk.
-  writer.reset();
-
-  VerifyPackets(kNumAdditionalPackets);
-}
-
-TEST_P(StartupTraceWriterTest, MaxBufferSizeExceeded) {
-  auto writer = CreateUnboundWriter();
-
-  // Write packets until the buffer is extended above kMaxBufferSizeBytes.
-  size_t valid_packets = 0;
-  while (GetTotalBufferSize(writer.get()) < max_buffer_size_bytes()) {
-    WritePackets(writer.get(), 1);
-    valid_packets++;
-  }
-
-  // The next packet should be dropped into a void.
-  WritePackets(writer.get(), 1);
-
-  // Binding the writer should cause the valid previously written packets to be
-  // written to the SMB and committed.
-  EXPECT_TRUE(BindWriter(writer.get()));
-
-  VerifyPackets(valid_packets, /*expect_data_loss_packet=*/true);
-
-  // Any further packets should be written to the SMB directly.
-  const size_t kNumAdditionalPackets = 16;
-  WritePackets(writer.get(), kNumAdditionalPackets);
-  // Finalizes the last packet and returns the chunk.
-  writer.reset();
-
-  VerifyPackets(kNumAdditionalPackets);
-}
-
-TEST_P(StartupTraceWriterTest, BindingWhileWritingFails) {
-  auto writer = CreateUnboundWriter();
-
-  {
-    // Begin a write by opening a TracePacket.
-    auto packet = writer->NewTracePacket();
-    packet->set_for_testing()->set_str(kPacketPayload);
-
-    // Binding while writing should fail.
-    EXPECT_FALSE(BindWriter(writer.get()));
-  }
-
-  // Packet was completed, so binding should work now and emit the packet.
-  EXPECT_TRUE(BindWriter(writer.get()));
-  VerifyPackets(1);
-}
-
-TEST_P(StartupTraceWriterTest, CreateAndBindViaRegistry) {
-  std::unique_ptr<StartupTraceWriterRegistry> registry(
-      new StartupTraceWriterRegistry());
-
-  // Create unbound writers.
-  auto writer1 =
-      registry->CreateUnboundTraceWriter(BufferExhaustedPolicy::kDrop);
-  auto writer2 =
-      registry->CreateUnboundTraceWriter(BufferExhaustedPolicy::kDrop);
-
-  EXPECT_EQ(2u, GetUnboundWriterCount(*registry));
-
-  // Return |writer2|. It should be kept alive until the registry is bound.
-  StartupTraceWriter::ReturnToRegistry(std::move(writer2));
-
-  {
-    // Begin a write by opening a TracePacket on |writer1|.
-    auto packet = writer1->NewTracePacket();
-
-    // Binding |writer1| writing should fail, but |writer2| should be bound.
-    const BufferID kBufId = 42;
-    arbiter_->BindStartupTraceWriterRegistry(std::move(registry), kBufId);
-    EXPECT_EQ(1u, GetUnboundWriterCount(*arbiter_));
-  }
-
-  // Wait for |writer1| to be bound and the registry to be deleted.
-  auto checkpoint_name = "all_bound";
-  auto all_bound = task_runner_->CreateCheckpoint(checkpoint_name);
-  std::function<void()> task;
-  task = [&task, &all_bound, this]() {
-    if (!GetBindingRegistriesCount(*arbiter_)) {
-      all_bound();
-      return;
-    }
-    task_runner_->PostDelayedTask(task, 1);
-  };
-  task_runner_->PostDelayedTask(task, 1);
-  task_runner_->RunUntilCheckpoint(checkpoint_name);
-
-  StartupTraceWriter::ReturnToRegistry(std::move(writer1));
-}
-
-TEST_P(StartupTraceWriterTest, BindAndCommitInBatches) {
-  auto writer = CreateUnboundWriter();
-
-  // Write a single packet to determine its size in the buffer.
-  WritePackets(writer.get(), 1);
-  size_t packet_size = writer->used_buffer_size();
-
-  // Write at least 3 pages/chunks worth of packets.
-  const size_t kNumPackets = (page_size() * 3 + packet_size - 1) / packet_size;
-  WritePackets(writer.get(), kNumPackets);
-
-  static constexpr size_t kChunksPerBatch = 2;
-
-  // Binding the writer with a batch size of 2 chunks should cause the first 2
-  // chunks of previously written packets to be written to the SMB and
-  // committed. The remaining chunks will be written when the
-  // |commit_data_callback| is executed.
-  EXPECT_TRUE(BindWriter(writer.get(), kChunksPerBatch));
-
-  EXPECT_EQ(
-      fake_producer_endpoint_.last_commit_data_request.chunks_to_move().size(),
-      kChunksPerBatch);
-  EXPECT_EQ(CountCompleteChunksInSMB(), kChunksPerBatch);
-  auto commit_data_callback = fake_producer_endpoint_.last_commit_data_callback;
-  EXPECT_TRUE(commit_data_callback);
-
-  // Send a commit with a single packet from the bound trace writer before the
-  // remaining chunk batches of the buffered data are written.
-  const size_t kNumAdditionalPackets = 1;
-  WritePackets(writer.get(), 1);
-  // Finalizes the packet and returns the chunk.
-  writer.reset();
-
-  // The packet should fit into a chunk.
-  EXPECT_EQ(
-      fake_producer_endpoint_.last_commit_data_request.chunks_to_move().size(),
-      1u);
-  EXPECT_EQ(CountCompleteChunksInSMB(), kChunksPerBatch + 1);
-
-  // Write and commit the remaining chunks to the SMB.
-  while (commit_data_callback) {
-    commit_data_callback();
-    commit_data_callback = fake_producer_endpoint_.last_commit_data_callback;
-  }
-
-  // Verify that all chunks + packets are in the SMB.
-  VerifyPackets(1 + kNumPackets + kNumAdditionalPackets);
-}
-
-TEST_P(StartupTraceWriterTest, BindAndCommitInBatchesWithSMBExhaustion) {
-  auto writer = CreateUnboundWriter();
-
-  // Write a single packet to determine its size in the buffer.
-  WritePackets(writer.get(), 1);
-  size_t packet_size = writer->used_buffer_size();
-
-  // Write at least 3 pages/chunks worth of packets.
-  const size_t kNumPackets = (page_size() * 3 + packet_size - 1) / packet_size;
-  WritePackets(writer.get(), kNumPackets);
-
-  // Acquire all chunks in the SMB.
-  static constexpr size_t kTotChunks = kNumPages;
-  SharedMemoryABI::Chunk chunks[kTotChunks];
-  for (size_t i = 0; i < kTotChunks; i++) {
-    chunks[i] = arbiter_->GetNewChunk({}, BufferExhaustedPolicy::kDrop);
-    ASSERT_TRUE(chunks[i].is_valid());
-  }
-
-  // Binding the writer should fail if no chunks are available in the SMB.
-  static constexpr size_t kChunksPerBatch = 2;
-  EXPECT_FALSE(BindWriter(writer.get(), kChunksPerBatch));
-
-  // Return and free the first chunk, so that there is only a single free chunk.
-  PatchList ignored;
-  arbiter_->ReturnCompletedChunk(std::move(chunks[0]), 0, &ignored);
-  chunks[0] =
-      arbiter_->shmem_abi_for_testing()->TryAcquireChunkForReading(0, 0);
-  ASSERT_TRUE(chunks[0].is_valid());
-  arbiter_->shmem_abi_for_testing()->ReleaseChunkAsFree(std::move(chunks[0]));
-  arbiter_->FlushPendingCommitDataRequests();
-
-  // Binding the writer should only cause the first chunks of previously written
-  // packets to be written to the SMB and committed because no further chunks
-  // are available in the SMB. The remaining chunks will be written when the
-  // |commit_data_callback| is executed.
-  EXPECT_TRUE(BindWriter(writer.get(), kChunksPerBatch));
-
-  EXPECT_EQ(
-      fake_producer_endpoint_.last_commit_data_request.chunks_to_move().size(),
-      1u);
-  EXPECT_EQ(CountCompleteChunksInSMB(), 1u);
-  auto commit_data_callback = fake_producer_endpoint_.last_commit_data_callback;
-  EXPECT_TRUE(commit_data_callback);
-
-  // Free up the other SMB chunks.
-  for (size_t i = 1; i < kTotChunks; i++) {
-    arbiter_->ReturnCompletedChunk(std::move(chunks[i]), 0, &ignored);
-    chunks[i] =
-        arbiter_->shmem_abi_for_testing()->TryAcquireChunkForReading(i, 0);
-    ASSERT_TRUE(chunks[i].is_valid());
-    arbiter_->shmem_abi_for_testing()->ReleaseChunkAsFree(std::move(chunks[i]));
-  }
-  arbiter_->FlushPendingCommitDataRequests();
-
-  // Write and commit the remaining buffered startup writer data to the SMB.
-  while (commit_data_callback) {
-    commit_data_callback();
-    commit_data_callback = fake_producer_endpoint_.last_commit_data_callback;
-  }
-  EXPECT_GT(
-      fake_producer_endpoint_.last_commit_data_request.chunks_to_move().size(),
-      0u);
-
-  // Verify that all chunks + packets are in the SMB.
-  VerifyPackets(1 + kNumPackets);
-}
-
-}  // namespace
-}  // namespace perfetto
diff --git a/src/tracing/core/trace_buffer.cc b/src/tracing/core/trace_buffer.cc
index fe7ff48..2227870 100644
--- a/src/tracing/core/trace_buffer.cc
+++ b/src/tracing/core/trace_buffer.cc
@@ -880,6 +880,12 @@
                         chunk_meta->is_complete())) {
     stats_.set_chunks_read(stats_.chunks_read() + 1);
     stats_.set_bytes_read(stats_.bytes_read() + chunk_meta->chunk_record->size);
+  } else {
+    // We have at least one more packet to parse. It should be within the chunk.
+    if (chunk_meta->cur_fragment_offset + sizeof(ChunkRecord) >=
+        chunk_meta->chunk_record->size) {
+      PERFETTO_DCHECK(suppress_sanity_dchecks_for_testing_);
+    }
   }
 
   chunk_meta->set_last_read_packet_skipped(false);
diff --git a/src/tracing/core/trace_writer_impl.cc b/src/tracing/core/trace_writer_impl.cc
index b18f99c..f0705ae 100644
--- a/src/tracing/core/trace_writer_impl.cc
+++ b/src/tracing/core/trace_writer_impl.cc
@@ -42,13 +42,14 @@
 
 TraceWriterImpl::TraceWriterImpl(SharedMemoryArbiterImpl* shmem_arbiter,
                                  WriterID id,
-                                 BufferID target_buffer,
+                                 MaybeUnboundBufferID target_buffer,
                                  BufferExhaustedPolicy buffer_exhausted_policy)
     : shmem_arbiter_(shmem_arbiter),
       id_(id),
       target_buffer_(target_buffer),
       buffer_exhausted_policy_(buffer_exhausted_policy),
-      protobuf_stream_writer_(this) {
+      protobuf_stream_writer_(this),
+      process_id_(base::GetProcessId()) {
   // TODO(primiano): we could handle the case of running out of TraceWriterID(s)
   // more gracefully and always return a no-op TracePacket in NewTracePacket().
   PERFETTO_CHECK(id_ != 0);
@@ -79,12 +80,20 @@
   // for the sake of getting the callback posted back.
   shmem_arbiter_->FlushPendingCommitDataRequests(callback);
   protobuf_stream_writer_.Reset({nullptr, nullptr});
+
+  // |last_packet_size_field_| might have pointed into the chunk we returned.
+  last_packet_size_field_ = nullptr;
 }
 
 TraceWriterImpl::TracePacketHandle TraceWriterImpl::NewTracePacket() {
   // If we hit this, the caller is calling NewTracePacket() without having
   // finalized the previous packet.
   PERFETTO_CHECK(cur_packet_->is_finalized());
+  // If we hit this, this trace writer was created in a different process. This
+  // likely means that the process forked while tracing was active, and the
+  // forked child process tried to emit a trace event. This is not supported, as
+  // it would lead to two processes writing to the same tracing SMB.
+  PERFETTO_DCHECK(process_id_ == base::GetProcessId());
 
   fragmenting_packet_ = false;
 
@@ -349,19 +358,8 @@
   return id_;
 }
 
-bool TraceWriterImpl::SetFirstChunkId(ChunkID chunk_id) {
-  if (next_chunk_id_ > 0)
-    return false;
-  next_chunk_id_ = chunk_id;
-  return true;
-}
-
 // Base class definitions.
 TraceWriter::TraceWriter() = default;
 TraceWriter::~TraceWriter() = default;
 
-bool TraceWriter::SetFirstChunkId(ChunkID) {
-  return false;
-}
-
 }  // namespace perfetto
diff --git a/src/tracing/core/trace_writer_impl.h b/src/tracing/core/trace_writer_impl.h
index 691f5fb..be2daef 100644
--- a/src/tracing/core/trace_writer_impl.h
+++ b/src/tracing/core/trace_writer_impl.h
@@ -17,6 +17,7 @@
 #ifndef SRC_TRACING_CORE_TRACE_WRITER_IMPL_H_
 #define SRC_TRACING_CORE_TRACE_WRITER_IMPL_H_
 
+#include "perfetto/base/proc_utils.h"
 #include "perfetto/ext/tracing/core/basic_types.h"
 #include "perfetto/ext/tracing/core/shared_memory_abi.h"
 #include "perfetto/ext/tracing/core/shared_memory_arbiter.h"
@@ -38,7 +39,7 @@
   // TracePacketHandle is defined in trace_writer.h
   TraceWriterImpl(SharedMemoryArbiterImpl*,
                   WriterID,
-                  BufferID,
+                  MaybeUnboundBufferID buffer_id,
                   BufferExhaustedPolicy);
   ~TraceWriterImpl() override;
 
@@ -46,7 +47,6 @@
   TracePacketHandle NewTracePacket() override;
   void Flush(std::function<void()> callback = {}) override;
   WriterID writer_id() const override;
-  bool SetFirstChunkId(ChunkID) override;
   uint64_t written() const override {
     return protobuf_stream_writer_.written();
   }
@@ -68,9 +68,11 @@
   // ID of the current writer.
   const WriterID id_;
 
-  // This is just copied back into the chunk header.
-  // See comments in data_source_config.proto for |target_buffer|.
-  const BufferID target_buffer_;
+  // This is copied into the commit request by SharedMemoryArbiter. See comments
+  // in data_source_config.proto for |target_buffer|. If this is a reservation
+  // for a buffer ID in case of a startup trace writer, SharedMemoryArbiterImpl
+  // will also translate the reservation ID to the actual buffer ID.
+  const MaybeUnboundBufferID target_buffer_;
 
   // Whether GetNewChunk() should stall or return an invalid chunk if the SMB is
   // exhausted.
@@ -127,6 +129,10 @@
   // later sent out-of-band to the tracing service, who will patch the required
   // chunks, if they are still around.
   PatchList patch_list_;
+
+  // PID of the process that created the trace writer. Used for a DCHECK that
+  // aims to detect unsupported process forks while tracing.
+  const base::PlatformProcessId process_id_;
 };
 
 }  // namespace perfetto
diff --git a/src/tracing/core/trace_writer_impl_unittest.cc b/src/tracing/core/trace_writer_impl_unittest.cc
index 00f7c0c..b40a172 100644
--- a/src/tracing/core/trace_writer_impl_unittest.cc
+++ b/src/tracing/core/trace_writer_impl_unittest.cc
@@ -248,6 +248,108 @@
                SharedMemoryABI::ChunkHeader::kLastPacketContinuesOnNextChunk);
 }
 
+// Verifies that a TraceWriter that is flushed before the SMB is full and then
+// acquires a garbage chunk later recovers and writes a previous_packet_dropped
+// marker into the trace.
+TEST_P(TraceWriterImplTest, FlushBeforeBufferExhausted) {
+  arbiter_.reset(new SharedMemoryArbiterImpl(buf(), buf_size(), page_size(),
+                                             &fake_producer_endpoint_,
+                                             task_runner_.get()));
+
+  const BufferID kBufId = 42;
+  std::unique_ptr<TraceWriter> writer =
+      arbiter_->CreateTraceWriter(kBufId, BufferExhaustedPolicy::kDrop);
+
+  // Write a small first packet and flush it, so that |writer| no longer owns
+  // any chunk.
+  auto packet = writer->NewTracePacket();
+  EXPECT_FALSE(reinterpret_cast<TraceWriterImpl*>(writer.get())
+                   ->drop_packets_for_testing());
+  EXPECT_EQ(packet->Finalize(), 0u);
+
+  // Flush the first chunk away.
+  writer->Flush();
+
+  // First chunk should be committed. Don't release it as free just yet.
+  arbiter_->FlushPendingCommitDataRequests();
+  const auto& last_commit = fake_producer_endpoint_.last_commit_data_request;
+  ASSERT_EQ(1, last_commit.chunks_to_move_size());
+  EXPECT_EQ(0u, last_commit.chunks_to_move()[0].page());
+  EXPECT_EQ(0u, last_commit.chunks_to_move()[0].chunk());
+
+  // Grab all the remaining chunks in the SMB in new writers.
+  std::array<std::unique_ptr<TraceWriter>, kNumPages * 4 - 1> other_writers;
+  for (size_t i = 0; i < other_writers.size(); i++) {
+    other_writers[i] =
+        arbiter_->CreateTraceWriter(kBufId, BufferExhaustedPolicy::kDrop);
+    auto other_writer_packet = other_writers[i]->NewTracePacket();
+    EXPECT_FALSE(reinterpret_cast<TraceWriterImpl*>(other_writers[i].get())
+                     ->drop_packets_for_testing());
+  }
+
+  // Write another packet, causing |writer| to acquire a garbage chunk.
+  auto packet2 = writer->NewTracePacket();
+  EXPECT_TRUE(reinterpret_cast<TraceWriterImpl*>(writer.get())
+                  ->drop_packets_for_testing());
+
+  // Writing more data while in garbage mode succeeds. This data is dropped.
+  // Make sure that we fill the garbage chunk, so that |writer| tries to
+  // re-acquire a valid chunk for the next packet.
+  size_t chunk_size = page_size() / 4;
+  std::stringstream large_string_writer;
+  for (size_t pos = 0; pos < chunk_size; pos++)
+    large_string_writer << "x";
+  std::string large_string = large_string_writer.str();
+  packet2->set_for_testing()->set_str(large_string.data(), large_string.size());
+  packet2->Finalize();
+
+  // Next packet should still be in the garbage chunk.
+  auto packet3 = writer->NewTracePacket();
+  EXPECT_TRUE(reinterpret_cast<TraceWriterImpl*>(writer.get())
+                  ->drop_packets_for_testing());
+
+  // Release the first chunk as free, so |writer| can acquire it again.
+  SharedMemoryABI* abi = arbiter_->shmem_abi_for_testing();
+  ASSERT_EQ(SharedMemoryABI::kChunkComplete, abi->GetChunkState(0u, 0u));
+  auto chunk = abi->TryAcquireChunkForReading(0u, 0u);
+  ASSERT_TRUE(chunk.is_valid());
+  abi->ReleaseChunkAsFree(std::move(chunk));
+
+  // Fill the garbage chunk, so that the writer attempts to grab another chunk
+  // for |packet4|.
+  packet3->set_for_testing()->set_str(large_string.data(), large_string.size());
+  packet3->Finalize();
+
+  // Next packet should go into the reacquired chunk we just released.
+  auto packet4 = writer->NewTracePacket();
+  EXPECT_FALSE(reinterpret_cast<TraceWriterImpl*>(writer.get())
+                   ->drop_packets_for_testing());
+
+  // The first packet in the chunk should have the previous_packet_dropped flag
+  // set, so shouldn't be empty.
+  EXPECT_GT(packet4->Finalize(), 0u);
+
+  // Flushing the writer causes the chunk to be released again.
+  writer->Flush();
+  EXPECT_EQ(1, last_commit.chunks_to_move_size());
+  EXPECT_EQ(0u, last_commit.chunks_to_move()[0].page());
+  EXPECT_EQ(0u, last_commit.chunks_to_move()[0].chunk());
+  ASSERT_EQ(0, last_commit.chunks_to_patch_size());
+
+  // Chunk should contain only |packet4| and not have any continuation flag set.
+  ASSERT_EQ(SharedMemoryABI::kChunkComplete, abi->GetChunkState(0u, 0u));
+  chunk = abi->TryAcquireChunkForReading(0u, 0u);
+  ASSERT_TRUE(chunk.is_valid());
+  ASSERT_EQ(1, chunk.header()->packets.load().count);
+  ASSERT_FALSE(chunk.header()->packets.load().flags &
+               SharedMemoryABI::ChunkHeader::kChunkNeedsPatching);
+  ASSERT_FALSE(
+      chunk.header()->packets.load().flags &
+      SharedMemoryABI::ChunkHeader::kFirstPacketContinuesFromPrevChunk);
+  ASSERT_FALSE(chunk.header()->packets.load().flags &
+               SharedMemoryABI::ChunkHeader::kLastPacketContinuesOnNextChunk);
+}
+
 // TODO(primiano): add multi-writer test.
 // TODO(primiano): add Flush() test.
 
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index 394b784..edba84c 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -36,15 +36,24 @@
 #include <sys/system_properties.h>
 #endif
 
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
+#define PERFETTO_HAS_CHMOD
+#include <sys/stat.h>
+#endif
+
 #include <algorithm>
 
 #include "perfetto/base/build_config.h"
 #include "perfetto/base/task_runner.h"
 #include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/metatrace.h"
+#include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/utils.h"
 #include "perfetto/ext/base/watchdog.h"
 #include "perfetto/ext/tracing/core/consumer.h"
+#include "perfetto/ext/tracing/core/observable_events.h"
 #include "perfetto/ext/tracing/core/producer.h"
 #include "perfetto/ext/tracing/core/shared_memory.h"
 #include "perfetto/ext/tracing/core/shared_memory_abi.h"
@@ -53,6 +62,7 @@
 #include "perfetto/protozero/scattered_heap_buffer.h"
 #include "perfetto/protozero/static_buffer.h"
 #include "perfetto/tracing/core/data_source_descriptor.h"
+#include "perfetto/tracing/core/tracing_service_capabilities.h"
 #include "perfetto/tracing/core/tracing_service_state.h"
 #include "src/tracing/core/packet_stream_validator.h"
 #include "src/tracing/core/shared_memory_arbiter_impl.h"
@@ -61,6 +71,7 @@
 #include "protos/perfetto/common/trace_stats.pbzero.h"
 #include "protos/perfetto/config/trace_config.pbzero.h"
 #include "protos/perfetto/trace/clock_snapshot.pbzero.h"
+#include "protos/perfetto/trace/perfetto/tracing_service_event.pbzero.h"
 #include "protos/perfetto/trace/system_info.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 #include "protos/perfetto/trace/trigger.pbzero.h"
@@ -149,6 +160,80 @@
   packets->back().AddSlice(std::move(slice));
 }
 
+std::tuple<size_t /*shm_size*/, size_t /*page_size*/> EnsureValidShmSizes(
+    size_t shm_size,
+    size_t page_size) {
+  // Theoretically the max page size supported by the ABI is 64KB.
+  // However, the current implementation of TraceBuffer (the non-shared
+  // userspace buffer where the service copies data) supports at most
+  // 32K. Setting 64K "works" from the producer<>consumer viewpoint
+  // but then causes the data to be discarded when copying it into
+  // TraceBuffer.
+  constexpr size_t kMaxPageSize = 32 * 1024;
+  static_assert(kMaxPageSize <= SharedMemoryABI::kMaxPageSize, "");
+
+  if (page_size == 0)
+    page_size = TracingServiceImpl::kDefaultShmPageSize;
+  if (shm_size == 0)
+    shm_size = TracingServiceImpl::kDefaultShmSize;
+
+  page_size = std::min<size_t>(page_size, kMaxPageSize);
+  shm_size = std::min<size_t>(shm_size, TracingServiceImpl::kMaxShmSize);
+
+  // Page size has to be multiple of system's page size.
+  bool page_size_is_valid = page_size >= base::kPageSize;
+  page_size_is_valid &= page_size % base::kPageSize == 0;
+
+  // Only allow power of two numbers of pages, i.e. 1, 2, 4, 8 pages.
+  size_t num_pages = page_size / base::kPageSize;
+  page_size_is_valid &= (num_pages & (num_pages - 1)) == 0;
+
+  if (!page_size_is_valid || shm_size < page_size ||
+      shm_size % page_size != 0) {
+    return std::make_tuple(TracingServiceImpl::kDefaultShmSize,
+                           TracingServiceImpl::kDefaultShmPageSize);
+  }
+  return std::make_tuple(shm_size, page_size);
+}
+
+bool NameMatchesFilter(const std::string& name,
+                       const std::vector<std::string>& name_filter,
+                       const std::vector<std::string>& name_regex_filter) {
+  bool filter_is_set = !name_filter.empty() || !name_regex_filter.empty();
+  if (!filter_is_set)
+    return true;
+  bool filter_matches = std::find(name_filter.begin(), name_filter.end(),
+                                  name) != name_filter.end();
+  bool filter_regex_matches =
+      std::find_if(name_regex_filter.begin(), name_regex_filter.end(),
+                   [&](const std::string& regex) {
+                     return std::regex_match(
+                         name, std::regex(regex, std::regex::extended));
+                   }) != name_regex_filter.end();
+  return filter_matches || filter_regex_matches;
+}
+
+// Used when write_into_file == true and output_path is not empty.
+base::ScopedFile CreateTraceFile(const std::string& path) {
+#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+  static const char kBase[] = "/data/misc/perfetto-traces/";
+  if (!base::StartsWith(path, kBase) || path.rfind('/') != strlen(kBase) - 1) {
+    PERFETTO_ELOG("Invalid output_path %s. On Android it must be within %s.",
+                  path.c_str(), kBase);
+    return base::ScopedFile();
+  }
+#endif
+  // O_CREAT | O_EXCL will fail if the file exists already.
+  auto fd = base::OpenFile(path, O_RDWR | O_CREAT | O_EXCL, 0600);
+  if (!fd)
+    PERFETTO_PLOG("Failed to create %s", path.c_str());
+#if defined(PERFETTO_HAS_CHMOD)
+  // Passing 0644 directly above won't work because of umask.
+  PERFETTO_CHECK(fchmod(*fd, 0644) == 0);
+#endif
+  return fd;
+}
+
 }  // namespace
 
 // These constants instead are defined in the header because are used by tests.
@@ -189,7 +274,8 @@
                                     size_t shared_memory_size_hint_bytes,
                                     bool in_process,
                                     ProducerSMBScrapingMode smb_scraping_mode,
-                                    size_t shared_memory_page_size_hint_bytes) {
+                                    size_t shared_memory_page_size_hint_bytes,
+                                    std::unique_ptr<SharedMemory> shm) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
 
   if (lockdown_mode_ && uid != geteuid()) {
@@ -224,8 +310,39 @@
   PERFETTO_DCHECK(it_and_inserted.second);
   endpoint->shmem_size_hint_bytes_ = shared_memory_size_hint_bytes;
   endpoint->shmem_page_size_hint_bytes_ = shared_memory_page_size_hint_bytes;
+
+  // Producer::OnConnect() should run before Producer::OnTracingSetup(). The
+  // latter may be posted by SetupSharedMemory() below, so post OnConnect() now.
   task_runner_->PostTask(std::bind(&Producer::OnConnect, endpoint->producer_));
 
+  if (shm) {
+    // The producer supplied an SMB. This is used only by Chrome; in the most
+    // common cases the SMB is created by the service and passed via
+    // OnTracingSetup(). Verify that it is correctly sized before we attempt to
+    // use it. The transport layer has to verify the integrity of the SMB (e.g.
+    // ensure that the producer can't resize if after the fact).
+    size_t shm_size, page_size;
+    std::tie(shm_size, page_size) =
+        EnsureValidShmSizes(shm->size(), endpoint->shmem_page_size_hint_bytes_);
+    if (shm_size == shm->size() &&
+        page_size == endpoint->shmem_page_size_hint_bytes_) {
+      PERFETTO_DLOG(
+          "Adopting producer-provided SMB of %zu kB for producer \"%s\"",
+          shm_size / 1024, endpoint->name_.c_str());
+      endpoint->SetupSharedMemory(std::move(shm), page_size,
+                                  /*provided_by_producer=*/true);
+    } else {
+      PERFETTO_LOG(
+          "Discarding incorrectly sized producer-provided SMB for producer "
+          "\"%s\", falling back to service-provided SMB. Requested sizes: %zu "
+          "B total, %zu B page size; suggested corrected sizes: %zu B total, "
+          "%zu B page size",
+          endpoint->name_.c_str(), shm->size(),
+          endpoint->shmem_page_size_hint_bytes_, shm_size, page_size);
+      shm.reset();
+    }
+  }
+
   return std::unique_ptr<ProducerEndpoint>(std::move(endpoint));
 }
 
@@ -496,12 +613,20 @@
            .first->second;
 
   if (cfg.write_into_file()) {
-    if (!fd) {
+    if (!fd ^ !cfg.output_path().empty()) {
       PERFETTO_ELOG(
-          "The TraceConfig had write_into_file==true but no fd was passed");
+          "When write_into_file==true either a FD needs to be passed or "
+          "output_path must be populated (but not both)");
       tracing_sessions_.erase(tsid);
       return false;
     }
+    if (!cfg.output_path().empty()) {
+      fd = CreateTraceFile(cfg.output_path());
+      if (!fd) {
+        tracing_sessions_.erase(tsid);
+        return false;
+      }
+    }
     tracing_session->write_into_file = std::move(fd);
     uint32_t write_period_ms = cfg.file_write_period_ms();
     if (write_period_ms == 0)
@@ -615,10 +740,12 @@
 
   tracing_session->state = TracingSession::CONFIGURED;
   PERFETTO_LOG(
-      "Configured tracing, #sources:%zu, duration:%d ms, #buffers:%d, total "
-      "buffer size:%zu KB, total sessions:%zu session name: %s",
-      cfg.data_sources().size(), tracing_session->config.duration_ms(),
+      "Configured tracing session %" PRIu64
+      ", #sources:%zu, duration:%d ms, #buffers:%d, total "
+      "buffer size:%zu KB, total sessions:%zu, uid:%d session name: \"%s\"",
+      tsid, cfg.data_sources().size(), tracing_session->config.duration_ms(),
       cfg.buffers_size(), total_buf_size_kb, tracing_sessions_.size(),
+      static_cast<unsigned int>(consumer->uid_),
       cfg.unique_session_name().c_str());
 
   // Start the data sources, unless this is a case of early setup + fast
@@ -646,33 +773,38 @@
     return;
   }
 
-  // We only support updating producer_name_filter (and pass-through configs)
-  // for now; null out any changeable fields and make sure the rest are
+  // We only support updating producer_name_{,regex}_filter (and pass-through
+  // configs) for now; null out any changeable fields and make sure the rest are
   // identical.
   TraceConfig new_config_copy(updated_cfg);
   for (auto& ds_cfg : *new_config_copy.mutable_data_sources()) {
     ds_cfg.clear_producer_name_filter();
+    ds_cfg.clear_producer_name_regex_filter();
   }
 
   TraceConfig current_config_copy(tracing_session->config);
-  for (auto& ds_cfg : *current_config_copy.mutable_data_sources())
+  for (auto& ds_cfg : *current_config_copy.mutable_data_sources()) {
     ds_cfg.clear_producer_name_filter();
+    ds_cfg.clear_producer_name_regex_filter();
+  }
 
   if (new_config_copy != current_config_copy) {
     PERFETTO_LOG(
         "ChangeTraceConfig() was called with a config containing unsupported "
-        "changes; only adding to the producer_name_filter is currently "
-        "supported and will have an effect.");
+        "changes; only adding to the producer_name_{,regex}_filter is "
+        "currently supported and will have an effect.");
   }
 
   for (TraceConfig::DataSource& cfg_data_source :
        *tracing_session->config.mutable_data_sources()) {
     // Find the updated producer_filter in the new config.
     std::vector<std::string> new_producer_name_filter;
+    std::vector<std::string> new_producer_name_regex_filter;
     bool found_data_source = false;
     for (auto it : updated_cfg.data_sources()) {
       if (cfg_data_source.config().name() == it.config().name()) {
         new_producer_name_filter = it.producer_name_filter();
+        new_producer_name_regex_filter = it.producer_name_regex_filter();
         found_data_source = true;
         break;
       }
@@ -682,8 +814,7 @@
     if (!found_data_source) {
       PERFETTO_ELOG(
           "ChangeTraceConfig() called without a current data source also "
-          "present in the new "
-          "config: %s",
+          "present in the new config: %s",
           cfg_data_source.config().name().c_str());
       continue;
     }
@@ -694,6 +825,8 @@
     // producers will keep producing but newly added producers after this
     // point will never start.
     *cfg_data_source.mutable_producer_name_filter() = new_producer_name_filter;
+    *cfg_data_source.mutable_producer_name_regex_filter() =
+        new_producer_name_regex_filter;
 
     // Scan all the registered data sources with a matching name.
     auto range = data_sources_.equal_range(cfg_data_source.config().name());
@@ -702,12 +835,10 @@
       PERFETTO_DCHECK(producer);
 
       // Check if the producer name of this data source is present
-      // in the name filter. We currently only support new filters, not removing
-      // old ones.
-      if (!new_producer_name_filter.empty() &&
-          std::find(new_producer_name_filter.begin(),
-                    new_producer_name_filter.end(),
-                    producer->name_) == new_producer_name_filter.end()) {
+      // in the name filters. We currently only support new filters, not
+      // removing old ones.
+      if (!NameMatchesFilter(producer->name_, new_producer_name_filter,
+                             new_producer_name_regex_filter)) {
         continue;
       }
 
@@ -843,6 +974,10 @@
         *producer, *instance);
   }
   producer->StartDataSource(instance->instance_id, instance->config);
+
+  // If all data sources are started, notify the consumer.
+  if (instance->state == DataSourceInstance::STARTED)
+    MaybeNotifyAllDataSourcesStarted(tracing_session);
 }
 
 // DisableTracing just stops the data sources but doesn't free up any buffer.
@@ -953,9 +1088,37 @@
       tracing_session.consumer_maybe_null->OnDataSourceInstanceStateChange(
           *producer, *instance);
     }
+
+    // If all data sources are started, notify the consumer.
+    MaybeNotifyAllDataSourcesStarted(&tracing_session);
   }  // for (tracing_session)
 }
 
+void TracingServiceImpl::MaybeNotifyAllDataSourcesStarted(
+    TracingSession* tracing_session) {
+  if (!tracing_session->consumer_maybe_null)
+    return;
+
+  if (!tracing_session->AllDataSourceInstancesStarted())
+    return;
+
+  // In some rare cases, we can get in this state more than once. Consider the
+  // following scenario: 3 data sources are registered -> trace starts ->
+  // all 3 data sources ack -> OnAllDataSourcesStarted() is called.
+  // Imagine now that a 4th data source registers while the trace is ongoing.
+  // This would hit the AllDataSourceInstancesStarted() condition again.
+  // In this case, however, we don't want to re-notify the consumer again.
+  // That would be unexpected (even if, perhaps, technically correct) and
+  // trigger bugs in the consumer.
+  if (tracing_session->did_notify_all_data_source_started)
+    return;
+
+  PERFETTO_DLOG("All data sources started");
+  tracing_session->did_notify_all_data_source_started = true;
+  tracing_session->time_all_data_source_started = base::GetBootTimeNs();
+  tracing_session->consumer_maybe_null->OnAllDataSourcesStarted();
+}
+
 void TracingServiceImpl::NotifyDataSourceStopped(
     ProducerID producer_id,
     DataSourceInstanceID instance_id) {
@@ -1018,8 +1181,9 @@
       // (non-empty producer_name()) ensure the producer who sent this trigger
       // matches.
       if (!iter->producer_name_regex().empty() &&
-          !std::regex_match(producer->name_,
-                            std::regex(iter->producer_name_regex()))) {
+          !std::regex_match(
+              producer->name_,
+              std::regex(iter->producer_name_regex(), std::regex::extended))) {
         continue;
       }
 
@@ -1535,6 +1699,8 @@
   }
   if (!tracing_session->config.builtin_data_sources().disable_system_info())
     MaybeEmitSystemInfo(tracing_session, &packets);
+  if (!tracing_session->config.builtin_data_sources().disable_service_events())
+    MaybeEmitServiceEvents(tracing_session, &packets);
 
   size_t packets_bytes = 0;  // SUM(slice.size() for each slice in |packets|).
   size_t total_slices = 0;   // SUM(#slices in |packets|).
@@ -1845,6 +2011,7 @@
   PERFETTO_DCHECK(producer);
   for (auto& kv : tracing_sessions_) {
     auto& ds_instances = kv.second.data_source_instances;
+    bool removed = false;
     for (auto it = ds_instances.begin(); it != ds_instances.end();) {
       if (it->first == producer_id && it->second.data_source_name == name) {
         DataSourceInstanceID ds_inst_id = it->second.instance_id;
@@ -1858,11 +2025,14 @@
             NotifyDataSourceStopped(producer_id, ds_inst_id);
         }
         it = ds_instances.erase(it);
+        removed = true;
       } else {
         ++it;
       }
     }  // for (data_source_instances)
-  }    // for (tracing_session)
+    if (removed)
+      MaybeNotifyAllDataSourcesStarted(&kv.second);
+  }  // for (tracing_session)
 
   for (auto it = data_sources_.begin(); it != data_sources_.end(); ++it) {
     if (it->second.producer_id == producer_id &&
@@ -1892,20 +2062,15 @@
     PERFETTO_DLOG("Lockdown mode: not enabling producer %hu", producer->id_);
     return nullptr;
   }
-  // TODO(primiano): Add tests for registration ordering
-  // (data sources vs consumers).
-  // TODO: This logic is duplicated in ChangeTraceConfig, consider refactoring
-  // it. Meanwhile update both.
-  if (!cfg_data_source.producer_name_filter().empty()) {
-    if (std::find(cfg_data_source.producer_name_filter().begin(),
-                  cfg_data_source.producer_name_filter().end(),
-                  producer->name_) ==
-        cfg_data_source.producer_name_filter().end()) {
-      PERFETTO_DLOG("Data source: %s is filtered out for producer: %s",
-                    cfg_data_source.config().name().c_str(),
-                    producer->name_.c_str());
-      return nullptr;
-    }
+  // TODO(primiano): Add tests for registration ordering (data sources vs
+  // consumers).
+  if (!NameMatchesFilter(producer->name_,
+                         cfg_data_source.producer_name_filter(),
+                         cfg_data_source.producer_name_regex_filter())) {
+    PERFETTO_DLOG("Data source: %s is filtered out for producer: %s",
+                  cfg_data_source.config().name().c_str(),
+                  producer->name_.c_str());
+    return nullptr;
   }
 
   auto relative_buffer_id = cfg_data_source.config().target_buffer();
@@ -1961,15 +2126,9 @@
     // 1. Give priority to what is defined in the trace config.
     // 2. If unset give priority to the hint passed by the producer.
     // 3. Keep within bounds and ensure it's a multiple of 4k.
-    size_t page_size = std::min<size_t>(producer_config.page_size_kb() * 1024,
-                                        SharedMemoryABI::kMaxPageSize);
-    if (page_size == 0) {
-      page_size = std::min<size_t>(producer->shmem_page_size_hint_bytes_,
-                                   SharedMemoryABI::kMaxPageSize);
-    }
-    if (page_size < base::kPageSize || page_size % base::kPageSize != 0)
-      page_size = kDefaultShmPageSize;
-    producer->shared_buffer_page_size_kb_ = page_size / 1024;
+    size_t page_size = producer_config.page_size_kb() * 1024;
+    if (page_size == 0)
+      page_size = producer->shmem_page_size_hint_bytes_;
 
     // Determine the SMB size. Must be an integer multiple of the SMB page size.
     // The decision tree is as follows:
@@ -1979,9 +2138,16 @@
     size_t shm_size = producer_config.shm_size_kb() * 1024;
     if (shm_size == 0)
       shm_size = producer->shmem_size_hint_bytes_;
-    shm_size = std::min<size_t>(shm_size, kMaxShmSize);
-    if (shm_size < page_size || shm_size % page_size)
-      shm_size = kDefaultShmSize;
+
+    auto valid_sizes = EnsureValidShmSizes(shm_size, page_size);
+    if (valid_sizes != std::tie(shm_size, page_size)) {
+      PERFETTO_DLOG(
+          "Invalid configured SMB sizes: shm_size %zu page_size %zu. Falling "
+          "back to shm_size %zu page_size %zu.",
+          shm_size, page_size, std::get<0>(valid_sizes),
+          std::get<1>(valid_sizes));
+    }
+    std::tie(shm_size, page_size) = valid_sizes;
 
     // TODO(primiano): right now Create() will suicide in case of OOM if the
     // mmap fails. We should instead gracefully fail the request and tell the
@@ -1989,9 +2155,8 @@
     PERFETTO_DLOG("Creating SMB of %zu KB for producer \"%s\"", shm_size / 1024,
                   producer->name_.c_str());
     auto shared_memory = shm_factory_->CreateSharedMemory(shm_size);
-    producer->SetSharedMemory(std::move(shared_memory));
-    producer->OnTracingSetup();
-    UpdateMemoryGuardrail();
+    producer->SetupSharedMemory(std::move(shared_memory), page_size,
+                                /*provided_by_producer=*/false);
   }
   producer->SetupDataSource(inst_id, ds_config);
   return ds_instance;
@@ -2381,12 +2546,28 @@
   } else {
     PERFETTO_ELOG("Unable to read ro.build.fingerprint");
   }
+  info->set_hz(sysconf(_SC_CLK_TCK));
 #endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
   packet->set_trusted_uid(static_cast<int32_t>(uid_));
   packet->set_trusted_packet_sequence_id(kServicePacketSequenceID);
   SerializeAndAppendPacket(packets, packet.SerializeAsArray());
 }
 
+void TracingServiceImpl::MaybeEmitServiceEvents(
+    TracingSession* tracing_session,
+    std::vector<TracePacket>* packets) {
+  int64_t all_start_ns = tracing_session->time_all_data_source_started.count();
+  if (!tracing_session->did_emit_all_data_source_started && all_start_ns > 0) {
+    tracing_session->did_emit_all_data_source_started = true;
+    protozero::HeapBuffered<protos::pbzero::TracePacket> packet;
+    packet->set_timestamp(static_cast<uint64_t>(all_start_ns));
+    packet->set_trusted_uid(static_cast<int32_t>(uid_));
+    packet->set_trusted_packet_sequence_id(kServicePacketSequenceID);
+    packet->set_service_event()->set_all_data_sources_started(true);
+    SerializeAndAppendPacket(packets, packet.SerializeAsArray());
+  }
+}
+
 void TracingServiceImpl::MaybeEmitReceivedTriggers(
     TracingSession* tracing_session,
     std::vector<TracePacket>* packets) {
@@ -2552,33 +2733,35 @@
 }
 
 void TracingServiceImpl::ConsumerEndpointImpl::ObserveEvents(
-    uint32_t enabled_event_types) {
+    uint32_t events_mask) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  enabled_observable_event_types_ = enabled_event_types;
-
-  if (enabled_observable_event_types_ == ObservableEventType::kNone)
-    return;
-
-  PERFETTO_DCHECK(enabled_observable_event_types_ ==
-                  ObservableEventType::kDataSourceInstances);
-
+  observable_events_mask_ = events_mask;
   TracingSession* session = service_->GetTracingSession(tracing_session_id_);
   if (!session)
     return;
 
-  // Issue initial states
-  for (const auto& kv : session->data_source_instances) {
-    ProducerEndpointImpl* producer = service_->GetProducer(kv.first);
-    PERFETTO_DCHECK(producer);
-    OnDataSourceInstanceStateChange(*producer, kv.second);
+  if (observable_events_mask_ & ObservableEvents::TYPE_DATA_SOURCES_INSTANCES) {
+    // Issue initial states.
+    for (const auto& kv : session->data_source_instances) {
+      ProducerEndpointImpl* producer = service_->GetProducer(kv.first);
+      PERFETTO_DCHECK(producer);
+      OnDataSourceInstanceStateChange(*producer, kv.second);
+    }
+  }
+
+  // If the ObserveEvents() call happens after data sources have acked already
+  // notify immediately.
+  if (observable_events_mask_ &
+      ObservableEvents::TYPE_ALL_DATA_SOURCES_STARTED) {
+    service_->MaybeNotifyAllDataSourcesStarted(session);
   }
 }
 
 void TracingServiceImpl::ConsumerEndpointImpl::OnDataSourceInstanceStateChange(
     const ProducerEndpointImpl& producer,
     const DataSourceInstance& instance) {
-  if (!(enabled_observable_event_types_ &
-        ObservableEventType::kDataSourceInstances)) {
+  if (!(observable_events_mask_ &
+        ObservableEvents::TYPE_DATA_SOURCES_INSTANCES)) {
     return;
   }
 
@@ -2599,6 +2782,15 @@
   }
 }
 
+void TracingServiceImpl::ConsumerEndpointImpl::OnAllDataSourcesStarted() {
+  if (!(observable_events_mask_ &
+        ObservableEvents::TYPE_ALL_DATA_SOURCES_STARTED)) {
+    return;
+  }
+  auto* observable_events = AddObservableEvents();
+  observable_events->set_all_data_sources_started(true);
+}
+
 base::WeakPtr<TracingServiceImpl::ConsumerEndpointImpl>
 TracingServiceImpl::ConsumerEndpointImpl::GetWeakPtr() {
   PERFETTO_DCHECK_THREAD(thread_checker_);
@@ -2653,6 +2845,20 @@
   callback(/*success=*/true, svc_state);
 }
 
+void TracingServiceImpl::ConsumerEndpointImpl::QueryCapabilities(
+    QueryCapabilitiesCallback callback) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  TracingServiceCapabilities caps;
+  caps.set_has_query_capabilities(true);
+  caps.set_has_trace_config_output_path(true);
+  caps.add_observable_events(ObservableEvents::TYPE_DATA_SOURCES_INSTANCES);
+  caps.add_observable_events(ObservableEvents::TYPE_ALL_DATA_SOURCES_STARTED);
+  static_assert(ObservableEvents::Type_MAX ==
+                    ObservableEvents::TYPE_ALL_DATA_SOURCES_STARTED,
+                "");
+  callback(caps);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // TracingServiceImpl::ProducerEndpointImpl implementation
 ////////////////////////////////////////////////////////////////////////////////
@@ -2702,7 +2908,6 @@
     uint32_t writer_id,
     uint32_t target_buffer) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  PERFETTO_DCHECK(!buffer_id_for_writer(static_cast<WriterID>(writer_id)));
   writers_[static_cast<WriterID>(writer_id)] =
       static_cast<BufferID>(target_buffer);
 }
@@ -2710,7 +2915,6 @@
 void TracingServiceImpl::ProducerEndpointImpl::UnregisterTraceWriter(
     uint32_t writer_id) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  PERFETTO_DCHECK(buffer_id_for_writer(static_cast<WriterID>(writer_id)));
   writers_.erase(static_cast<WriterID>(writer_id));
 }
 
@@ -2777,10 +2981,17 @@
     callback();
 }
 
-void TracingServiceImpl::ProducerEndpointImpl::SetSharedMemory(
-    std::unique_ptr<SharedMemory> shared_memory) {
+void TracingServiceImpl::ProducerEndpointImpl::SetupSharedMemory(
+    std::unique_ptr<SharedMemory> shared_memory,
+    size_t page_size_bytes,
+    bool provided_by_producer) {
   PERFETTO_DCHECK(!shared_memory_ && !shmem_abi_.is_valid());
+  PERFETTO_DCHECK(page_size_bytes % 1024 == 0);
+
   shared_memory_ = std::move(shared_memory);
+  shared_buffer_page_size_kb_ = page_size_bytes / 1024;
+  is_shmem_provided_by_producer_ = provided_by_producer;
+
   shmem_abi_.Initialize(reinterpret_cast<uint8_t*>(shared_memory_->start()),
                         shared_memory_->size(),
                         shared_buffer_page_size_kb() * 1024);
@@ -2789,6 +3000,9 @@
         shared_memory_->start(), shared_memory_->size(),
         shared_buffer_page_size_kb_ * 1024, this, task_runner_));
   }
+
+  OnTracingSetup();
+  service_->UpdateMemoryGuardrail();
 }
 
 SharedMemory* TracingServiceImpl::ProducerEndpointImpl::shared_memory() const {
@@ -2820,7 +3034,7 @@
 }
 
 SharedMemoryArbiter*
-TracingServiceImpl::ProducerEndpointImpl::GetInProcessShmemArbiter() {
+TracingServiceImpl::ProducerEndpointImpl::MaybeSharedMemoryArbiter() {
   if (!inproc_shmem_arbiter_) {
     PERFETTO_FATAL(
         "The in-process SharedMemoryArbiter can only be used when "
@@ -2832,19 +3046,26 @@
   return inproc_shmem_arbiter_.get();
 }
 
+bool TracingServiceImpl::ProducerEndpointImpl::IsShmemProvidedByProducer()
+    const {
+  return is_shmem_provided_by_producer_;
+}
+
 // Can be called on any thread.
 std::unique_ptr<TraceWriter>
 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() {
@@ -2923,6 +3144,11 @@
   });
 }
 
+void TracingServiceImpl::ProducerEndpointImpl::Sync(
+    std::function<void()> callback) {
+  task_runner_->PostTask(callback);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // TracingServiceImpl::TracingSession implementation
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/tracing/core/tracing_service_impl.h b/src/tracing/core/tracing_service_impl.h
index 61c6ef6..2eb07a6 100644
--- a/src/tracing/core/tracing_service_impl.h
+++ b/src/tracing/core/tracing_service_impl.h
@@ -17,6 +17,7 @@
 #ifndef SRC_TRACING_CORE_TRACING_SERVICE_IMPL_H_
 #define SRC_TRACING_CORE_TRACING_SERVICE_IMPL_H_
 
+#include <algorithm>
 #include <functional>
 #include <map>
 #include <memory>
@@ -86,17 +87,21 @@
                              uint32_t target_buffer) override;
     void UnregisterTraceWriter(uint32_t writer_id) override;
     void CommitData(const CommitDataRequest&, CommitDataCallback) override;
-    void SetSharedMemory(std::unique_ptr<SharedMemory>);
+    void SetupSharedMemory(std::unique_ptr<SharedMemory>,
+                           size_t page_size_bytes,
+                           bool provided_by_producer);
     std::unique_ptr<TraceWriter> CreateTraceWriter(
         BufferID,
         BufferExhaustedPolicy) override;
-    SharedMemoryArbiter* GetInProcessShmemArbiter() override;
+    SharedMemoryArbiter* MaybeSharedMemoryArbiter() override;
+    bool IsShmemProvidedByProducer() const override;
     void NotifyFlushComplete(FlushRequestID) override;
     void NotifyDataSourceStarted(DataSourceInstanceID) override;
     void NotifyDataSourceStopped(DataSourceInstanceID) override;
     SharedMemory* shared_memory() const override;
     size_t shared_buffer_page_size_kb() const override;
     void ActivateTriggers(const std::vector<std::string>&) override;
+    void Sync(std::function<void()> callback) override;
 
     void OnTracingSetup();
     void SetupDataSource(DataSourceInstanceID, const DataSourceConfig&);
@@ -136,6 +141,7 @@
     SharedMemoryABI shmem_abi_;
     size_t shmem_size_hint_bytes_ = 0;
     size_t shmem_page_size_hint_bytes_ = 0;
+    bool is_shmem_provided_by_producer_ = false;
     const std::string name_;
     bool in_process_;
     bool smb_scraping_enabled_;
@@ -187,11 +193,12 @@
     void GetTraceStats() override;
     void ObserveEvents(uint32_t enabled_event_types) override;
     void QueryServiceState(QueryServiceStateCallback) override;
+    void QueryCapabilities(QueryCapabilitiesCallback) override;
 
-    // If |observe_data_source_instances == true|, will queue a task to notify
-    // the consumer about the state change.
+    // Will queue a task to notify the consumer about the state change.
     void OnDataSourceInstanceStateChange(const ProducerEndpointImpl&,
                                          const DataSourceInstance&);
+    void OnAllDataSourcesStarted();
 
    private:
     friend class TracingServiceImpl;
@@ -210,7 +217,8 @@
 
     // Whether the consumer is interested in DataSourceInstance state change
     // events.
-    uint32_t enabled_observable_event_types_ = ObservableEventType::kNone;
+    uint32_t observable_events_mask_ = 0;
+
     // ObservableEvents that will be sent to the consumer. If set, a task to
     // flush the events to the consumer has been queued.
     std::unique_ptr<ObservableEvents> observable_events_;
@@ -271,7 +279,8 @@
       bool in_process = false,
       ProducerSMBScrapingMode smb_scraping_mode =
           ProducerSMBScrapingMode::kDefault,
-      size_t shared_memory_page_size_hint_bytes = 0) override;
+      size_t shared_memory_page_size_hint_bytes = 0,
+      std::unique_ptr<SharedMemory> shm = nullptr) override;
 
   std::unique_ptr<TracingService::ConsumerEndpoint> ConnectConsumer(
       Consumer*,
@@ -397,12 +406,20 @@
       return nullptr;
     }
 
+    bool AllDataSourceInstancesStarted() {
+      return std::all_of(
+          data_source_instances.begin(), data_source_instances.end(),
+          [](decltype(data_source_instances)::const_reference x) {
+            return x.second.state == DataSourceInstance::STARTED;
+          });
+    }
+
     bool AllDataSourceInstancesStopped() {
-      for (const auto& inst_kv : data_source_instances) {
-        if (inst_kv.second.state != DataSourceInstance::STOPPED)
-          return false;
-      }
-      return true;
+      return std::all_of(
+          data_source_instances.begin(), data_source_instances.end(),
+          [](decltype(data_source_instances)::const_reference x) {
+            return x.second.state == DataSourceInstance::STOPPED;
+          });
     }
 
     const TracingSessionID id;
@@ -472,6 +489,11 @@
     // Packets that failed validation of the TrustedPacket.
     uint64_t invalid_packets = 0;
 
+    // Set to true on the first call to OnAllDataSourcesStarted().
+    bool did_notify_all_data_source_started = false;
+    bool did_emit_all_data_source_started = false;
+    base::TimeNanos time_all_data_source_started = {};
+
     // Initial clock snapshot, captured at trace start time (when state goes
     // to TracingSession::STARTED). Emitted into the trace when the consumer
     // first begins reading the trace.
@@ -527,9 +549,11 @@
   void SnapshotClocks(std::vector<TracePacket>*, bool set_root_timestamp);
   void SnapshotStats(TracingSession*, std::vector<TracePacket>*);
   TraceStats GetTraceStats(TracingSession* tracing_session);
+  void MaybeEmitServiceEvents(TracingSession*, std::vector<TracePacket>*);
   void MaybeEmitTraceConfig(TracingSession*, std::vector<TracePacket>*);
   void MaybeEmitSystemInfo(TracingSession*, std::vector<TracePacket>*);
   void MaybeEmitReceivedTriggers(TracingSession*, std::vector<TracePacket>*);
+  void MaybeNotifyAllDataSourcesStarted(TracingSession*);
   void OnFlushTimeout(TracingSessionID, FlushRequestID);
   void OnDisableTracingTimeout(TracingSessionID);
   void DisableTracingNotifyConsumerAndFlushFile(TracingSession*);
diff --git a/src/tracing/core/tracing_service_impl_unittest.cc b/src/tracing/core/tracing_service_impl_unittest.cc
index bedb3f1..60193c1 100644
--- a/src/tracing/core/tracing_service_impl_unittest.cc
+++ b/src/tracing/core/tracing_service_impl_unittest.cc
@@ -22,6 +22,7 @@
 #include "perfetto/ext/base/temp_file.h"
 #include "perfetto/ext/base/utils.h"
 #include "perfetto/ext/tracing/core/consumer.h"
+#include "perfetto/ext/tracing/core/observable_events.h"
 #include "perfetto/ext/tracing/core/producer.h"
 #include "perfetto/ext/tracing/core/shared_memory.h"
 #include "perfetto/ext/tracing/core/trace_packet.h"
@@ -36,6 +37,7 @@
 #include "src/tracing/test/test_shared_memory.h"
 #include "test/gtest_and_gmock.h"
 
+#include "protos/perfetto/trace/perfetto/tracing_service_event.gen.h"
 #include "protos/perfetto/trace/test_event.gen.h"
 #include "protos/perfetto/trace/test_event.pbzero.h"
 #include "protos/perfetto/trace/trace.gen.h"
@@ -297,7 +299,9 @@
 
   TraceConfig trace_config;
   trace_config.add_buffers()->set_size_kb(128);
-  auto* ds_config = trace_config.add_data_sources()->mutable_config();
+  auto* ds = trace_config.add_data_sources();
+  *ds->add_producer_name_regex_filter() = "mock_[p]roducer";
+  auto* ds_config = ds->mutable_config();
   ds_config->set_name("data_source");
   consumer->EnableTracing(trace_config);
 
@@ -1238,7 +1242,7 @@
 
   // Enable mock_producer_2, the third one should still
   // not get connected.
-  *data_source->add_producer_name_filter() = "mock_producer_2";
+  *data_source->add_producer_name_regex_filter() = ".*_producer_[2]";
   consumer->ChangeTraceConfig(trace_config);
 
   producer2->WaitForTracingSetup();
@@ -1384,12 +1388,13 @@
   producer->WaitForDataSourceStart("data_source");
 
   // The preamble packets are:
-  // Trace start clocksnapshot
+  // Trace start clock snapshot
   // Config
   // SystemInfo
-  // Trace read clocksnapshot
+  // Trace read clock snapshot
   // Trace synchronisation
-  static const int kNumPreamblePackets = 5;
+  // All data source started (TracingServiceEvent)
+  static const int kNumPreamblePackets = 6;
   static const int kNumTestPackets = 9;
   static const char kPayload[] = "1234567890abcdef-";
 
@@ -1433,48 +1438,115 @@
   }
 }
 
+TEST_F(TracingServiceImplTest, WriteIntoFileWithPath) {
+  auto tmp_file = base::TempFile::Create();
+  // Deletes the file (the service would refuse to overwrite an existing file)
+  // without telling it to the underlying TempFile, so that its dtor will
+  // unlink the file created by the service.
+  unlink(tmp_file.path().c_str());
+
+  std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+  consumer->Connect(svc.get());
+
+  std::unique_ptr<MockProducer> producer = CreateMockProducer();
+  producer->Connect(svc.get(), "mock_producer");
+  producer->RegisterDataSource("data_source");
+
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(4096);
+  auto* ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("data_source");
+  ds_config->set_target_buffer(0);
+  trace_config.set_write_into_file(true);
+  trace_config.set_output_path(tmp_file.path());
+  consumer->EnableTracing(trace_config);
+
+  producer->WaitForTracingSetup();
+  producer->WaitForDataSourceSetup("data_source");
+  producer->WaitForDataSourceStart("data_source");
+  std::unique_ptr<TraceWriter> writer =
+      producer->CreateTraceWriter("data_source");
+
+  {
+    auto tp = writer->NewTracePacket();
+    tp->set_for_testing()->set_str("payload");
+  }
+  writer->Flush();
+  writer.reset();
+
+  consumer->DisableTracing();
+  producer->WaitForDataSourceStop("data_source");
+  consumer->WaitForTracingDisabled();
+
+  // Verify the contents of the file.
+  std::string trace_raw;
+  ASSERT_TRUE(base::ReadFile(tmp_file.path(), &trace_raw));
+  protos::gen::Trace trace;
+  ASSERT_TRUE(trace.ParseFromString(trace_raw));
+  // ASSERT_EQ(trace.packet_size(), 33);
+  EXPECT_THAT(trace.packet(),
+              Contains(Property(
+                  &protos::gen::TracePacket::for_testing,
+                  Property(&protos::gen::TestEvent::str, Eq("payload")))));
+}
+
 // Test the logic that allows the trace config to set the shm total size and
 // page size from the trace config. Also check that, if the config doesn't
 // specify a value we fall back on the hint provided by the producer.
 TEST_F(TracingServiceImplTest, ProducerShmAndPageSizeOverriddenByTraceConfig) {
   std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
   consumer->Connect(svc.get());
-  const size_t kMaxPageSizeKb = SharedMemoryABI::kMaxPageSize / 1024;
-  const size_t kConfigPageSizesKb[] = /**/ {16, 0, 3, 2, 16, 8, 0, 4096, 0};
-  const size_t kPageHintSizesKb[] = /****/ {0, 4, 0, 0, 8, 0, 4096, 0, 0};
-  const size_t kExpectedPageSizesKb[] = {
-      16,                     // Use config value.
-      4,                      // Config is 0, use hint.
-      kDefaultShmPageSizeKb,  // Config % 4 != 0, take default.
-      kDefaultShmPageSizeKb,  // Less than page size, take default.
-      16,                     // Ignore the hint.
-      8,                      // Use config value.
-      kMaxPageSizeKb,         // Hint too big, take max value.
-      kMaxPageSizeKb,         // Config too high, take max value.
-      4                       // Fallback to default.
+  const size_t kMaxPageSizeKb = 32;
+
+  struct ConfiguredAndExpectedSizes {
+    size_t config_page_size_kb;
+    size_t hint_page_size_kb;
+    size_t expected_page_size_kb;
+
+    size_t config_size_kb;
+    size_t hint_size_kb;
+    size_t expected_size_kb;
   };
 
-  const size_t kConfigSizesKb[] = /**/ {0, 16, 0, 20, 32, 7, 0, 96, 4096000};
-  const size_t kHintSizesKb[] = /****/ {0, 0, 16, 32, 16, 0, 7, 96, 4096000};
-  const size_t kExpectedSizesKb[] = {
-      kDefaultShmSizeKb,  // Both hint and config are 0, use default.
-      16,                 // Hint is 0, use config.
-      16,                 // Config is 0, use hint.
-      20,                 // Hint is takes precedence over the config.
-      32,                 // Ditto, even if config is higher than hint.
-      kDefaultShmSizeKb,  // Config is invalid and hint is 0, use default.
-      kDefaultShmSizeKb,  // Config is 0 and hint is invalid, use default.
-      kDefaultShmSizeKb,  // 96 KB isn't a multiple of the page size (64 KB).
-      kMaxShmSizeKb       // Too big, cap at kMaxShmSize.
+  ConfiguredAndExpectedSizes kSizes[] = {
+      // Config and hint are 0, fallback to default values.
+      {0, 0, kDefaultShmPageSizeKb, 0, 0, kDefaultShmSizeKb},
+      // Use configured sizes.
+      {16, 0, 16, 16, 0, 16},
+      // Config is 0, use hint.
+      {0, 4, 4, 0, 16, 16},
+      // Config takes precendence over hint.
+      {4, 8, 4, 16, 32, 16},
+      // Config takes precendence over hint, even if it's larger.
+      {8, 4, 8, 32, 16, 32},
+      // Config page size % 4 != 0, fallback to defaults.
+      {3, 0, kDefaultShmPageSizeKb, 0, 0, kDefaultShmSizeKb},
+      // Config page size less than system page size, fallback to defaults.
+      {2, 0, kDefaultShmPageSizeKb, 0, 0, kDefaultShmSizeKb},
+      // Config sizes too large, use max.
+      {4096, 0, kMaxPageSizeKb, 4096000, 0, kMaxShmSizeKb},
+      // Hint sizes too large, use max.
+      {0, 4096, kMaxPageSizeKb, 0, 4096000, kMaxShmSizeKb},
+      // Config buffer size isn't a multiple of 4KB, fallback to defaults.
+      {0, 0, kDefaultShmPageSizeKb, 18, 0, kDefaultShmSizeKb},
+      // Invalid page size -> also ignore buffer size config.
+      {2, 0, kDefaultShmPageSizeKb, 32, 0, kDefaultShmSizeKb},
+      // Invalid buffer size -> also ignore page size config.
+      {16, 0, kDefaultShmPageSizeKb, 18, 0, kDefaultShmSizeKb},
+      // Config page size % buffer size != 0, fallback to defaults.
+      {8, 0, kDefaultShmPageSizeKb, 20, 0, kDefaultShmSizeKb},
+      // Config page size % default buffer size != 0, fallback to defaults.
+      {28, 0, kDefaultShmPageSizeKb, 0, 0, kDefaultShmSizeKb},
   };
 
-  const size_t kNumProducers = base::ArraySize(kHintSizesKb);
+  const size_t kNumProducers = base::ArraySize(kSizes);
   std::unique_ptr<MockProducer> producer[kNumProducers];
   for (size_t i = 0; i < kNumProducers; i++) {
     auto name = "mock_producer_" + std::to_string(i);
     producer[i] = CreateMockProducer();
-    producer[i]->Connect(svc.get(), name, geteuid(), kHintSizesKb[i] * 1024,
-                         kPageHintSizesKb[i] * 1024);
+    producer[i]->Connect(svc.get(), name, geteuid(),
+                         kSizes[i].hint_size_kb * 1024,
+                         kSizes[i].hint_page_size_kb * 1024);
     producer[i]->RegisterDataSource("data_source");
   }
 
@@ -1485,15 +1557,21 @@
   for (size_t i = 0; i < kNumProducers; i++) {
     auto* producer_config = trace_config.add_producers();
     producer_config->set_producer_name("mock_producer_" + std::to_string(i));
-    producer_config->set_shm_size_kb(static_cast<uint32_t>(kConfigSizesKb[i]));
+    producer_config->set_shm_size_kb(
+        static_cast<uint32_t>(kSizes[i].config_size_kb));
     producer_config->set_page_size_kb(
-        static_cast<uint32_t>(kConfigPageSizesKb[i]));
+        static_cast<uint32_t>(kSizes[i].config_page_size_kb));
   }
 
   consumer->EnableTracing(trace_config);
+  size_t expected_shm_sizes_kb[kNumProducers]{};
+  size_t expected_page_sizes_kb[kNumProducers]{};
   size_t actual_shm_sizes_kb[kNumProducers]{};
   size_t actual_page_sizes_kb[kNumProducers]{};
   for (size_t i = 0; i < kNumProducers; i++) {
+    expected_shm_sizes_kb[i] = kSizes[i].expected_size_kb;
+    expected_page_sizes_kb[i] = kSizes[i].expected_page_size_kb;
+
     producer[i]->WaitForTracingSetup();
     producer[i]->WaitForDataSourceSetup("data_source");
     actual_shm_sizes_kb[i] =
@@ -1504,8 +1582,8 @@
   for (size_t i = 0; i < kNumProducers; i++) {
     producer[i]->WaitForDataSourceStart("data_source");
   }
-  ASSERT_THAT(actual_page_sizes_kb, ElementsAreArray(kExpectedPageSizesKb));
-  ASSERT_THAT(actual_shm_sizes_kb, ElementsAreArray(kExpectedSizesKb));
+  ASSERT_THAT(actual_page_sizes_kb, ElementsAreArray(expected_page_sizes_kb));
+  ASSERT_THAT(actual_shm_sizes_kb, ElementsAreArray(expected_shm_sizes_kb));
 }
 
 TEST_F(TracingServiceImplTest, ExplicitFlush) {
@@ -2750,8 +2828,7 @@
   producer->WaitForDataSourceStart("data_source");
 
   // Calling ObserveEvents should cause an event for the initial instance state.
-  consumer->ObserveEvents(TracingService::ConsumerEndpoint::
-                              ObservableEventType::kDataSourceInstances);
+  consumer->ObserveEvents(ObservableEvents::TYPE_DATA_SOURCES_INSTANCES);
   {
     auto events = consumer->WaitForObservableEvents();
 
@@ -2816,8 +2893,7 @@
   producer->WaitForDataSourceStart("data_source");
 
   // Stop observing events.
-  consumer->ObserveEvents(
-      TracingService::ConsumerEndpoint::ObservableEventType::kNone);
+  consumer->ObserveEvents(0);
 
   // Disabling should now no longer cause events to be sent to the consumer.
   consumer->DisableTracing();
@@ -2846,8 +2922,7 @@
   producer->WaitForDataSourceStart("data_source");
 
   // Calling ObserveEvents should cause an event for the initial instance state.
-  consumer->ObserveEvents(TracingService::ConsumerEndpoint::
-                              ObservableEventType::kDataSourceInstances);
+  consumer->ObserveEvents(ObservableEvents::TYPE_DATA_SOURCES_INSTANCES);
   {
     ObservableEvents event;
     ObservableEvents::DataSourceInstanceStateChange* change =
@@ -2880,6 +2955,191 @@
   consumer->WaitForTracingDisabled();
 }
 
+TEST_F(TracingServiceImplTest, ObserveAllDataSourceStarted) {
+  std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+  consumer->Connect(svc.get());
+
+  std::unique_ptr<MockProducer> producer = CreateMockProducer();
+  producer->Connect(svc.get(), "mock_producer");
+  producer->RegisterDataSource("ds1", /*ack_stop=*/false, /*ack_start=*/true);
+  producer->RegisterDataSource("ds2", /*ack_stop=*/false, /*ack_start=*/true);
+
+  TraceConfig trace_config;
+  trace_config.set_deferred_start(true);
+  trace_config.add_buffers()->set_size_kb(128);
+  auto* ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("ds1");
+  ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("ds2");
+
+  for (int repetition = 0; repetition < 3; repetition++) {
+    consumer->EnableTracing(trace_config);
+
+    if (repetition == 0)
+      producer->WaitForTracingSetup();
+
+    producer->WaitForDataSourceSetup("ds1");
+    producer->WaitForDataSourceSetup("ds2");
+    task_runner.RunUntilIdle();
+
+    consumer->ObserveEvents(ObservableEvents::TYPE_ALL_DATA_SOURCES_STARTED);
+    consumer->StartTracing();
+    producer->WaitForDataSourceStart("ds1");
+    producer->WaitForDataSourceStart("ds2");
+
+    DataSourceInstanceID id1 = producer->GetDataSourceInstanceId("ds1");
+    producer->endpoint()->NotifyDataSourceStarted(id1);
+
+    // The notification shouldn't happen yet, ds2 has not acked.
+    task_runner.RunUntilIdle();
+    Mock::VerifyAndClearExpectations(consumer.get());
+
+    EXPECT_THAT(
+        consumer->ReadBuffers(),
+        Contains(Property(
+            &protos::gen::TracePacket::service_event,
+            Property(
+                &protos::gen::TracingServiceEvent::all_data_sources_started,
+                Eq(false)))));
+
+    DataSourceInstanceID id2 = producer->GetDataSourceInstanceId("ds2");
+    producer->endpoint()->NotifyDataSourceStarted(id2);
+
+    // Now the |all_data_sources_started| notification should be sent.
+
+    auto events = consumer->WaitForObservableEvents();
+    ObservableEvents::DataSourceInstanceStateChange change;
+    EXPECT_TRUE(events.all_data_sources_started());
+
+    // Disabling should cause an instance state change to STOPPED.
+    consumer->DisableTracing();
+    producer->WaitForDataSourceStop("ds1");
+    producer->WaitForDataSourceStop("ds2");
+    consumer->WaitForTracingDisabled();
+
+    EXPECT_THAT(
+        consumer->ReadBuffers(),
+        Contains(Property(
+            &protos::gen::TracePacket::service_event,
+            Property(
+                &protos::gen::TracingServiceEvent::all_data_sources_started,
+                Eq(true)))));
+    consumer->FreeBuffers();
+
+    task_runner.RunUntilIdle();
+
+    Mock::VerifyAndClearExpectations(consumer.get());
+    Mock::VerifyAndClearExpectations(producer.get());
+  }
+}
+
+// Similar to ObserveAllDataSourceStarted, but covers the case of some data
+// sources not supporting the |notify_on_start|.
+TEST_F(TracingServiceImplTest, ObserveAllDataSourceStartedOnlySomeWillAck) {
+  std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+  consumer->Connect(svc.get());
+
+  std::unique_ptr<MockProducer> producer = CreateMockProducer();
+  producer->Connect(svc.get(), "mock_producer");
+  producer->RegisterDataSource("ds1", /*ack_stop=*/false, /*ack_start=*/true);
+  producer->RegisterDataSource("ds2_no_ack");
+
+  TraceConfig trace_config;
+  trace_config.set_deferred_start(true);
+  trace_config.add_buffers()->set_size_kb(128);
+  auto* ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("ds1");
+  ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("ds2_no_ack");
+
+  for (int repetition = 0; repetition < 3; repetition++) {
+    consumer->EnableTracing(trace_config);
+
+    if (repetition == 0)
+      producer->WaitForTracingSetup();
+
+    producer->WaitForDataSourceSetup("ds1");
+    producer->WaitForDataSourceSetup("ds2_no_ack");
+    task_runner.RunUntilIdle();
+
+    consumer->ObserveEvents(ObservableEvents::TYPE_ALL_DATA_SOURCES_STARTED);
+    consumer->StartTracing();
+    producer->WaitForDataSourceStart("ds1");
+    producer->WaitForDataSourceStart("ds2_no_ack");
+
+    DataSourceInstanceID id1 = producer->GetDataSourceInstanceId("ds1");
+    producer->endpoint()->NotifyDataSourceStarted(id1);
+
+    auto events = consumer->WaitForObservableEvents();
+    ObservableEvents::DataSourceInstanceStateChange change;
+    EXPECT_TRUE(events.all_data_sources_started());
+
+    // Disabling should cause an instance state change to STOPPED.
+    consumer->DisableTracing();
+    producer->WaitForDataSourceStop("ds1");
+    producer->WaitForDataSourceStop("ds2_no_ack");
+    consumer->FreeBuffers();
+    consumer->WaitForTracingDisabled();
+
+    task_runner.RunUntilIdle();
+    Mock::VerifyAndClearExpectations(consumer.get());
+    Mock::VerifyAndClearExpectations(producer.get());
+  }
+}
+
+// Similar to ObserveAllDataSourceStarted, but covers the case of no data
+// sources supporting the |notify_on_start|. In this case the
+// TYPE_ALL_DATA_SOURCES_STARTED notification should be sent immediately after
+// calling Start().
+TEST_F(TracingServiceImplTest, ObserveAllDataSourceStartedNoAck) {
+  std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+  consumer->Connect(svc.get());
+
+  std::unique_ptr<MockProducer> producer = CreateMockProducer();
+  producer->Connect(svc.get(), "mock_producer");
+  producer->RegisterDataSource("ds1_no_ack");
+  producer->RegisterDataSource("ds2_no_ack");
+
+  TraceConfig trace_config;
+  trace_config.set_deferred_start(true);
+  trace_config.add_buffers()->set_size_kb(128);
+  auto* ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("ds1_no_ack");
+  ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("ds2_no_ack");
+
+  for (int repetition = 0; repetition < 3; repetition++) {
+    consumer->EnableTracing(trace_config);
+
+    if (repetition == 0)
+      producer->WaitForTracingSetup();
+
+    producer->WaitForDataSourceSetup("ds1_no_ack");
+    producer->WaitForDataSourceSetup("ds2_no_ack");
+    task_runner.RunUntilIdle();
+
+    consumer->ObserveEvents(ObservableEvents::TYPE_ALL_DATA_SOURCES_STARTED);
+    consumer->StartTracing();
+    producer->WaitForDataSourceStart("ds1_no_ack");
+    producer->WaitForDataSourceStart("ds2_no_ack");
+
+    auto events = consumer->WaitForObservableEvents();
+    ObservableEvents::DataSourceInstanceStateChange change;
+    EXPECT_TRUE(events.all_data_sources_started());
+
+    // Disabling should cause an instance state change to STOPPED.
+    consumer->DisableTracing();
+    producer->WaitForDataSourceStop("ds1_no_ack");
+    producer->WaitForDataSourceStop("ds2_no_ack");
+    consumer->FreeBuffers();
+    consumer->WaitForTracingDisabled();
+
+    task_runner.RunUntilIdle();
+    Mock::VerifyAndClearExpectations(consumer.get());
+    Mock::VerifyAndClearExpectations(producer.get());
+  }
+}
+
 TEST_F(TracingServiceImplTest, QueryServiceState) {
   std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
   consumer->Connect(svc.get());
@@ -2967,4 +3227,77 @@
   // The destruction of |consumers| will tear down and stop the good sessions.
 }
 
+TEST_F(TracingServiceImplTest, ProducerProvidedSMB) {
+  static constexpr size_t kShmSizeBytes = 1024 * 1024;
+  static constexpr size_t kShmPageSizeBytes = 4 * 1024;
+
+  std::unique_ptr<MockProducer> producer = CreateMockProducer();
+
+  TestSharedMemory::Factory factory;
+  auto shm = factory.CreateSharedMemory(kShmSizeBytes);
+  SharedMemory* shm_raw = shm.get();
+
+  // Service should adopt the SMB provided by the producer.
+  producer->Connect(svc.get(), "mock_producer", /*uid=*/42,
+                    /*shared_memory_size_hint_bytes=*/0, kShmPageSizeBytes,
+                    std::move(shm));
+  EXPECT_TRUE(producer->endpoint()->IsShmemProvidedByProducer());
+  EXPECT_NE(producer->endpoint()->MaybeSharedMemoryArbiter(), nullptr);
+  EXPECT_EQ(producer->endpoint()->shared_memory(), shm_raw);
+
+  producer->WaitForTracingSetup();
+  producer->RegisterDataSource("data_source");
+
+  std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+  consumer->Connect(svc.get());
+
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(128);
+  auto* ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("data_source");
+
+  consumer->EnableTracing(trace_config);
+  producer->WaitForDataSourceSetup("data_source");
+  producer->WaitForDataSourceStart("data_source");
+
+  // Verify that data written to the producer-provided SMB ends up in trace
+  // buffer correctly.
+  std::unique_ptr<TraceWriter> writer =
+      producer->CreateTraceWriter("data_source");
+  {
+    auto tp = writer->NewTracePacket();
+    tp->set_for_testing()->set_str("payload");
+  }
+
+  auto flush_request = consumer->Flush();
+  producer->WaitForFlush(writer.get());
+  ASSERT_TRUE(flush_request.WaitForReply());
+
+  consumer->DisableTracing();
+  producer->WaitForDataSourceStop("data_source");
+  consumer->WaitForTracingDisabled();
+  EXPECT_THAT(consumer->ReadBuffers(),
+              Contains(Property(
+                  &protos::gen::TracePacket::for_testing,
+                  Property(&protos::gen::TestEvent::str, Eq("payload")))));
+}
+
+TEST_F(TracingServiceImplTest, ProducerProvidedSMBInvalidSizes) {
+  static constexpr size_t kShmSizeBytes = 1024 * 1024;
+  static constexpr size_t kShmPageSizeBytes = 20 * 1024;
+
+  std::unique_ptr<MockProducer> producer = CreateMockProducer();
+
+  TestSharedMemory::Factory factory;
+  auto shm = factory.CreateSharedMemory(kShmSizeBytes);
+
+  // Service should not adopt the SMB provided by the producer, because the SMB
+  // size isn't a multiple of the page size.
+  producer->Connect(svc.get(), "mock_producer", /*uid=*/42,
+                    /*shared_memory_size_hint_bytes=*/0, kShmPageSizeBytes,
+                    std::move(shm));
+  EXPECT_FALSE(producer->endpoint()->IsShmemProvidedByProducer());
+  EXPECT_EQ(producer->endpoint()->shared_memory(), nullptr);
+}
+
 }  // namespace perfetto
diff --git a/src/tracing/core/virtual_destructors.cc b/src/tracing/core/virtual_destructors.cc
index 75fc806..2f8bb3c 100644
--- a/src/tracing/core/virtual_destructors.cc
+++ b/src/tracing/core/virtual_destructors.cc
@@ -30,8 +30,8 @@
 Consumer::~Consumer() = default;
 Producer::~Producer() = default;
 TracingService::~TracingService() = default;
-TracingService::ConsumerEndpoint::~ConsumerEndpoint() = default;
-TracingService::ProducerEndpoint::~ProducerEndpoint() = default;
+ConsumerEndpoint::~ConsumerEndpoint() = default;
+ProducerEndpoint::~ProducerEndpoint() = default;
 SharedMemory::~SharedMemory() = default;
 SharedMemory::Factory::~Factory() = default;
 SharedMemoryArbiter::~SharedMemoryArbiter() = default;
diff --git a/src/tracing/debug_annotation.cc b/src/tracing/debug_annotation.cc
index d98e6ce..81e889c 100644
--- a/src/tracing/debug_annotation.cc
+++ b/src/tracing/debug_annotation.cc
@@ -25,41 +25,6 @@
 namespace internal {
 
 void WriteDebugAnnotation(protos::pbzero::DebugAnnotation* annotation,
-                          bool value) {
-  annotation->set_bool_value(value);
-}
-
-void WriteDebugAnnotation(protos::pbzero::DebugAnnotation* annotation,
-                          uint64_t value) {
-  annotation->set_uint_value(value);
-}
-
-void WriteDebugAnnotation(protos::pbzero::DebugAnnotation* annotation,
-                          unsigned value) {
-  annotation->set_uint_value(value);
-}
-
-void WriteDebugAnnotation(protos::pbzero::DebugAnnotation* annotation,
-                          int64_t value) {
-  annotation->set_int_value(value);
-}
-
-void WriteDebugAnnotation(protos::pbzero::DebugAnnotation* annotation,
-                          int value) {
-  annotation->set_int_value(value);
-}
-
-void WriteDebugAnnotation(protos::pbzero::DebugAnnotation* annotation,
-                          double value) {
-  annotation->set_double_value(value);
-}
-
-void WriteDebugAnnotation(protos::pbzero::DebugAnnotation* annotation,
-                          float value) {
-  annotation->set_double_value(static_cast<double>(value));
-}
-
-void WriteDebugAnnotation(protos::pbzero::DebugAnnotation* annotation,
                           const char* value) {
   annotation->set_string_value(value);
 }
diff --git a/src/tracing/internal/in_process_tracing_backend.cc b/src/tracing/internal/in_process_tracing_backend.cc
index 7f6d350..378185c 100644
--- a/src/tracing/internal/in_process_tracing_backend.cc
+++ b/src/tracing/internal/in_process_tracing_backend.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "src/tracing/internal/in_process_tracing_backend.h"
+#include "perfetto/tracing/internal/in_process_tracing_backend.h"
 
 #include "perfetto/base/logging.h"
 #include "perfetto/base/task_runner.h"
@@ -42,6 +42,7 @@
   ~InProcessShm() override;
   void* start() const override;
   size_t size() const override;
+  int fd() const override;
 
  private:
   base::PagedMemory mem_;
@@ -66,6 +67,10 @@
   return mem_.size();
 }
 
+int InProcessShm::fd() const {
+  return -1;
+}
+
 InProcessShmFactory::~InProcessShmFactory() = default;
 std::unique_ptr<SharedMemory> InProcessShmFactory::CreateSharedMemory(
     size_t size) {
@@ -75,7 +80,7 @@
 }  // namespace
 
 // static
-InProcessTracingBackend* InProcessTracingBackend::GetInstance() {
+TracingBackend* InProcessTracingBackend::GetInstance() {
   static auto* instance = new InProcessTracingBackend();
   return instance;
 }
diff --git a/src/tracing/internal/system_tracing_backend.cc b/src/tracing/internal/system_tracing_backend.cc
index d8e89f2..987f956 100644
--- a/src/tracing/internal/system_tracing_backend.cc
+++ b/src/tracing/internal/system_tracing_backend.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "src/tracing/internal/system_tracing_backend.h"
+#include "perfetto/tracing/internal/system_tracing_backend.h"
 
 #include "perfetto/base/logging.h"
 #include "perfetto/base/task_runner.h"
@@ -26,7 +26,7 @@
 namespace internal {
 
 // static
-SystemTracingBackend* SystemTracingBackend::GetInstance() {
+TracingBackend* SystemTracingBackend::GetInstance() {
   static auto* instance = new SystemTracingBackend();
   return instance;
 }
diff --git a/src/tracing/internal/tracing_muxer_impl.cc b/src/tracing/internal/tracing_muxer_impl.cc
index a185f25..e3e0a43 100644
--- a/src/tracing/internal/tracing_muxer_impl.cc
+++ b/src/tracing/internal/tracing_muxer_impl.cc
@@ -37,8 +37,6 @@
 #include "perfetto/tracing/trace_writer_base.h"
 #include "perfetto/tracing/tracing.h"
 #include "perfetto/tracing/tracing_backend.h"
-#include "src/tracing/internal/in_process_tracing_backend.h"
-#include "src/tracing/internal/system_tracing_backend.h"
 
 namespace perfetto {
 namespace internal {
@@ -144,7 +142,7 @@
   PERFETTO_DCHECK_THREAD(thread_checker_);
   service_ = std::move(endpoint);
   // Observe data source instance events so we get notified when tracing starts.
-  service_->ObserveEvents(ConsumerEndpoint::kDataSourceInstances);
+  service_->ObserveEvents(ObservableEvents::TYPE_DATA_SOURCES_INSTANCES);
 }
 
 void TracingMuxerImpl::ConsumerImpl::OnConnect() {
@@ -421,6 +419,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();
@@ -438,15 +442,14 @@
   };
 
   if (args.backends & kSystemBackend) {
-#if (PERFETTO_BUILDFLAG(PERFETTO_IPC))
-    add_backend(SystemTracingBackend::GetInstance(), kSystemBackend);
-#else
-    PERFETTO_ELOG("System backend not supporteed in the current configuration");
-#endif
+    PERFETTO_CHECK(args.system_backend_factory_);
+    add_backend(args.system_backend_factory_(), kSystemBackend);
   }
 
-  if (args.backends & kInProcessBackend)
-    add_backend(InProcessTracingBackend::GetInstance(), kInProcessBackend);
+  if (args.backends & kInProcessBackend) {
+    PERFETTO_CHECK(args.in_process_backend_factory_);
+    add_backend(args.in_process_backend_factory_(), kInProcessBackend);
+  }
 
   if (args.backends & kCustomBackend) {
     PERFETTO_CHECK(args.custom_backend);
diff --git a/src/tracing/internal/track_event_internal.cc b/src/tracing/internal/track_event_internal.cc
index 9a84aeb..1ba8fa0 100644
--- a/src/tracing/internal/track_event_internal.cc
+++ b/src/tracing/internal/track_event_internal.cc
@@ -24,7 +24,7 @@
 #include "perfetto/tracing/track_event_category_registry.h"
 #include "perfetto/tracing/track_event_interned_data_index.h"
 #include "protos/perfetto/common/data_source_descriptor.gen.h"
-#include "protos/perfetto/trace/clock_snapshot.pbzero.h"
+#include "protos/perfetto/common/track_event_descriptor.pbzero.h"
 #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"
@@ -38,6 +38,9 @@
 namespace {
 
 std::atomic<perfetto::base::PlatformThreadId> g_main_thread;
+static constexpr const char kLegacySlowPrefix[] = "disabled-by-default-";
+static constexpr const char kSlowTag[] = "slow";
+static constexpr const char kDebugTag[] = "debug";
 
 struct InternedEventCategory
     : public TrackEventInternedDataIndex<
@@ -47,10 +50,11 @@
           SmallInternedDataTraits> {
   static void Add(protos::pbzero::InternedData* interned_data,
                   size_t iid,
-                  const char* value) {
+                  const char* value,
+                  size_t length) {
     auto category = interned_data->add_event_categories();
     category->set_iid(iid);
-    category->set_name(value);
+    category->set_name(value, length);
   }
 };
 
@@ -85,42 +89,76 @@
   }
 };
 
-constexpr protos::pbzero::ClockSnapshot::Clock::BuiltinClocks GetClockType() {
-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX) && \
-    !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  return protos::pbzero::ClockSnapshot::Clock::BOOTTIME;
-#else
-  return protos::pbzero::ClockSnapshot::Clock::MONOTONIC;
-#endif
+enum class MatchType { kExact, kPattern };
+
+bool NameMatchesPattern(const std::string& pattern,
+                        const std::string& name,
+                        MatchType match_type) {
+  // To avoid pulling in all of std::regex, for now we only support a single "*"
+  // wildcard at the end of the pattern.
+  size_t i = pattern.find('*');
+  if (i != std::string::npos) {
+    PERFETTO_DCHECK(i == pattern.size() - 1);
+    if (match_type != MatchType::kPattern)
+      return false;
+    return name.substr(0, i) == pattern.substr(0, i);
+  }
+  return name == pattern;
+}
+
+bool NameMatchesPatternList(const std::vector<std::string>& patterns,
+                            const std::string& name,
+                            MatchType match_type) {
+  for (const auto& pattern : patterns) {
+    if (NameMatchesPattern(pattern, name, match_type))
+      return true;
+  }
+  return false;
 }
 
 }  // namespace
 
 // static
 bool TrackEventInternal::Initialize(
+    const TrackEventCategoryRegistry& registry,
     bool (*register_data_source)(const DataSourceDescriptor&)) {
   if (!g_main_thread)
     g_main_thread = perfetto::base::GetThreadId();
 
-  perfetto::DataSourceDescriptor dsd;
-  // TODO(skyostil): Advertise the known categories.
+  DataSourceDescriptor dsd;
   dsd.set_name("track_event");
+
+  protozero::HeapBuffered<protos::pbzero::TrackEventDescriptor> ted;
+  for (size_t i = 0; i < registry.category_count(); i++) {
+    auto category = registry.GetCategory(i);
+    // Don't register group categories.
+    if (category->IsGroup())
+      continue;
+    auto cat = ted->add_available_categories();
+    cat->set_name(category->name);
+    if (category->description)
+      cat->set_description(category->description);
+    for (const auto& tag : category->tags) {
+      if (tag)
+        cat->add_tags(tag);
+    }
+    // Disabled-by-default categories get a "slow" tag.
+    if (!strncmp(category->name, kLegacySlowPrefix, strlen(kLegacySlowPrefix)))
+      cat->add_tags(kSlowTag);
+  }
+  dsd.set_track_event_descriptor_raw(ted.SerializeAsString());
+
   return register_data_source(dsd);
 }
 
 // static
 void TrackEventInternal::EnableTracing(
     const TrackEventCategoryRegistry& registry,
-    const DataSourceConfig& config,
+    const protos::gen::TrackEventConfig& config,
     uint32_t instance_index) {
   for (size_t i = 0; i < registry.category_count(); i++) {
-    // TODO(skyostil): Support the full category config syntax instead of
-    // just strict matching.
-    // TODO(skyostil): Support comma-separated categories.
-    if (config.legacy_config().empty() ||
-        config.legacy_config() == registry.GetCategory(i)->name) {
+    if (IsCategoryEnabled(registry, config, *registry.GetCategory(i)))
       registry.EnableCategoryForInstance(i, instance_index);
-    }
   }
 }
 
@@ -133,10 +171,100 @@
 }
 
 // static
+bool TrackEventInternal::IsCategoryEnabled(
+    const TrackEventCategoryRegistry& registry,
+    const protos::gen::TrackEventConfig& config,
+    const Category& category) {
+  // If this is a group category, check if any of its constituent categories are
+  // enabled. If so, then this one is enabled too.
+  if (category.IsGroup()) {
+    bool result = false;
+    category.ForEachGroupMember([&](const char* member_name, size_t name_size) {
+      for (size_t i = 0; i < registry.category_count(); i++) {
+        const auto ref_category = registry.GetCategory(i);
+        // Groups can't refer to other groups.
+        if (ref_category->IsGroup())
+          continue;
+        // Require an exact match.
+        if (ref_category->name_size() != name_size ||
+            strncmp(ref_category->name, member_name, name_size)) {
+          continue;
+        }
+        if (IsCategoryEnabled(registry, config, *ref_category)) {
+          result = true;
+          // Break ForEachGroupMember() loop.
+          return false;
+        }
+        break;
+      }
+      // No match found => keep iterating.
+      return true;
+    });
+    return result;
+  }
+
+  auto has_matching_tag = [&](std::function<bool(const char*)> matcher) {
+    for (const auto& tag : category.tags) {
+      if (!tag)
+        break;
+      if (matcher(tag))
+        return true;
+    }
+    // Legacy "disabled-by-default" categories automatically get the "slow" tag.
+    if (!strncmp(category.name, kLegacySlowPrefix, strlen(kLegacySlowPrefix)) &&
+        matcher(kSlowTag)) {
+      return true;
+    }
+    return false;
+  };
+
+  // First try exact matches, then pattern matches.
+  const std::array<MatchType, 2> match_types = {
+      {MatchType::kExact, MatchType::kPattern}};
+  for (auto match_type : match_types) {
+    // 1. Enabled categories.
+    if (NameMatchesPatternList(config.enabled_categories(), category.name,
+                               match_type)) {
+      return true;
+    }
+
+    // 2. Enabled tags.
+    if (has_matching_tag([&](const char* tag) {
+          return NameMatchesPatternList(config.enabled_tags(), tag, match_type);
+        })) {
+      return true;
+    }
+
+    // 3. Disabled categories.
+    if (NameMatchesPatternList(config.disabled_categories(), category.name,
+                               match_type)) {
+      return false;
+    }
+
+    // 4. Disabled tags.
+    if (has_matching_tag([&](const char* tag) {
+          if (config.disabled_tags_size()) {
+            return NameMatchesPatternList(config.disabled_tags(), tag,
+                                          match_type);
+          } else {
+            // The "slow" and "debug" tags are disabled by default.
+            return NameMatchesPattern(kSlowTag, tag, match_type) ||
+                   NameMatchesPattern(kDebugTag, tag, match_type);
+          }
+        })) {
+      return false;
+    }
+  }
+
+  // If nothing matched, enable the category by default.
+  return true;
+}
+
+// static
 uint64_t TrackEventInternal::GetTimeNs() {
-  if (GetClockType() == protos::pbzero::ClockSnapshot::Clock::BOOTTIME)
+  if (GetClockId() == protos::pbzero::ClockSnapshot::Clock::BOOTTIME)
     return static_cast<uint64_t>(perfetto::base::GetBootTimeNs().count());
-  PERFETTO_DCHECK(GetClockType() ==
+  PERFETTO_DCHECK(GetClockId() ==
                   protos::pbzero::ClockSnapshot::Clock::MONOTONIC);
   return static_cast<uint64_t>(perfetto::base::GetWallTimeNs().count());
 }
@@ -152,7 +280,7 @@
         trace_writer, timestamp,
         protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED);
     auto defaults = packet->set_trace_packet_defaults();
-    defaults->set_timestamp_clock_id(GetClockType());
+    defaults->set_timestamp_clock_id(GetClockId());
 
     // Establish the default track for this event sequence.
     auto track_defaults = defaults->set_track_event_defaults();
@@ -175,10 +303,10 @@
                                    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());
+  // TODO(skyostil): Stop emitting this for every event once the trace
+  // processor understands trace packet defaults.
+  if (GetClockId() != protos::pbzero::ClockSnapshot::Clock::BOOTTIME)
+    packet->set_timestamp_clock_id(GetClockId());
   packet->set_sequence_flags(seq_flags);
   return packet;
 }
@@ -187,12 +315,11 @@
 EventContext TrackEventInternal::WriteEvent(
     TraceWriterBase* trace_writer,
     TrackEventIncrementalState* incr_state,
-    const char* category,
+    const Category* category,
     const char* name,
-    perfetto::protos::pbzero::TrackEvent::Type type) {
-  PERFETTO_DCHECK(category);
+    perfetto::protos::pbzero::TrackEvent::Type type,
+    uint64_t timestamp) {
   PERFETTO_DCHECK(g_main_thread);
-  auto timestamp = GetTimeNs();
 
   if (incr_state->was_cleared) {
     incr_state->was_cleared = false;
@@ -202,14 +329,19 @@
   EventContext ctx(std::move(packet), incr_state);
 
   auto track_event = ctx.event();
-  track_event->set_type(type);
+  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.
-  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 (category && type != protos::pbzero::TrackEvent::TYPE_SLICE_END) {
+    category->ForEachGroupMember(
+        [&](const char* member_name, size_t name_size) {
+          size_t category_iid =
+              InternedEventCategory::Get(&ctx, member_name, name_size);
+          track_event->add_category_iids(category_iid);
+          return true;
+        });
   }
   if (name) {
     size_t name_iid = InternedEventName::Get(&ctx, name);
diff --git a/src/tracing/ipc/BUILD.gn b/src/tracing/ipc/BUILD.gn
new file mode 100644
index 0000000..93c97a6
--- /dev/null
+++ b/src/tracing/ipc/BUILD.gn
@@ -0,0 +1,55 @@
+# 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",
+    "memfd.cc",
+    "memfd.h",
+    "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.cc b/src/tracing/ipc/consumer/consumer_ipc_client_impl.cc
index 28d89e1..7d60e5e 100644
--- a/src/tracing/ipc/consumer/consumer_ipc_client_impl.cc
+++ b/src/tracing/ipc/consumer/consumer_ipc_client_impl.cc
@@ -307,10 +307,12 @@
   }
 
   protos::gen::ObserveEventsRequest req;
-  if (enabled_event_types & ObservableEventType::kDataSourceInstances) {
-    req.add_events_to_observe(
-        protos::gen::ObservableEvents::TYPE_DATA_SOURCES_INSTANCES);
+  for (uint32_t i = 0; i < 32; i++) {
+    const uint32_t event_id = 1u << i;
+    if (enabled_event_types & event_id)
+      req.add_events_to_observe(static_cast<ObservableEvents::Type>(event_id));
   }
+
   ipc::Deferred<protos::gen::ObserveEventsResponse> async_response;
   // The IPC layer guarantees that callbacks are destroyed after this object
   // is destroyed (by virtue of destroying the |consumer_port_|). In turn the
@@ -319,8 +321,8 @@
   async_response.Bind(
       [this](ipc::AsyncResult<protos::gen::ObserveEventsResponse> response) {
         // Skip empty response, which the service sends to close the stream.
-        if (!response->events().instance_state_changes().size()) {
-          PERFETTO_DCHECK(!response.has_more());
+        if (!response.has_more()) {
+          PERFETTO_DCHECK(!response->events().instance_state_changes().size());
           return;
         }
         consumer_->OnObservableEvents(response->events());
@@ -336,16 +338,77 @@
     return;
   }
 
+  auto it = pending_query_svc_reqs_.insert(pending_query_svc_reqs_.end(),
+                                           {std::move(callback), {}});
   protos::gen::QueryServiceStateRequest req;
   ipc::Deferred<protos::gen::QueryServiceStateResponse> async_response;
+  auto weak_this = weak_ptr_factory_.GetWeakPtr();
   async_response.Bind(
-      [callback](
-          ipc::AsyncResult<protos::gen::QueryServiceStateResponse> response) {
-        if (!response)
-          callback(false, TracingServiceState());
-        callback(true, response->service_state());
+      [weak_this,
+       it](ipc::AsyncResult<protos::gen::QueryServiceStateResponse> response) {
+        if (weak_this)
+          weak_this->OnQueryServiceStateResponse(std::move(response), it);
       });
   consumer_port_.QueryServiceState(req, std::move(async_response));
 }
 
+void ConsumerIPCClientImpl::OnQueryServiceStateResponse(
+    ipc::AsyncResult<protos::gen::QueryServiceStateResponse> response,
+    PendingQueryServiceRequests::iterator req_it) {
+  PERFETTO_DCHECK(req_it->callback);
+
+  if (!response) {
+    auto callback = std::move(req_it->callback);
+    pending_query_svc_reqs_.erase(req_it);
+    callback(false, TracingServiceState());
+    return;
+  }
+
+  // The QueryServiceState response can be split in several chunks if the
+  // service has several data sources. The client is supposed to merge all the
+  // replies. The easiest way to achieve this is to re-serialize the partial
+  // response and then re-decode the merged result in one shot.
+  std::vector<uint8_t>& merged_resp = req_it->merged_resp;
+  std::vector<uint8_t> part = response->service_state().SerializeAsArray();
+  merged_resp.insert(merged_resp.end(), part.begin(), part.end());
+
+  if (response.has_more())
+    return;
+
+  // All replies have been received. Decode the merged result and reply to the
+  // callback.
+  protos::gen::TracingServiceState svc_state;
+  bool ok = svc_state.ParseFromArray(merged_resp.data(), merged_resp.size());
+  if (!ok)
+    PERFETTO_ELOG("Failed to decode merged QueryServiceStateResponse");
+  auto callback = std::move(req_it->callback);
+  pending_query_svc_reqs_.erase(req_it);
+  callback(ok, std::move(svc_state));
+}
+
+void ConsumerIPCClientImpl::QueryCapabilities(
+    QueryCapabilitiesCallback callback) {
+  if (!connected_) {
+    PERFETTO_DLOG(
+        "Cannot QueryCapabilities(), not connected to tracing service");
+    return;
+  }
+
+  protos::gen::QueryCapabilitiesRequest req;
+  ipc::Deferred<protos::gen::QueryCapabilitiesResponse> async_response;
+  async_response.Bind(
+      [callback](
+          ipc::AsyncResult<protos::gen::QueryCapabilitiesResponse> response) {
+        if (!response) {
+          // If the IPC fails, we are talking to an older version of the service
+          // that didn't support QueryCapabilities at all. In this case return
+          // an empty capabilities message.
+          callback(TracingServiceCapabilities());
+        } else {
+          callback(response->capabilities());
+        }
+      });
+  consumer_port_.QueryCapabilities(req, std::move(async_response));
+}
+
 }  // namespace perfetto
diff --git a/src/tracing/ipc/consumer/consumer_ipc_client_impl.h b/src/tracing/ipc/consumer/consumer_ipc_client_impl.h
index e65f1fc..ff305f6 100644
--- a/src/tracing/ipc/consumer/consumer_ipc_client_impl.h
+++ b/src/tracing/ipc/consumer/consumer_ipc_client_impl.h
@@ -19,6 +19,7 @@
 
 #include <stdint.h>
 
+#include <list>
 #include <vector>
 
 #include "perfetto/ext/base/scoped_file.h"
@@ -29,6 +30,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 {
@@ -70,6 +72,7 @@
   void GetTraceStats() override;
   void ObserveEvents(uint32_t enabled_event_types) override;
   void QueryServiceState(QueryServiceStateCallback) override;
+  void QueryCapabilities(QueryCapabilitiesCallback) override;
 
   // ipc::ServiceProxy::EventListener implementation.
   // These methods are invoked by the IPC layer, which knows nothing about
@@ -78,10 +81,23 @@
   void OnDisconnect() override;
 
  private:
+  struct PendingQueryServiceRequest {
+    QueryServiceStateCallback callback;
+
+    // All the replies will be appended here until |has_more| == false.
+    std::vector<uint8_t> merged_resp;
+  };
+
+  // List because we need stable iterators.
+  using PendingQueryServiceRequests = std::list<PendingQueryServiceRequest>;
+
   void OnReadBuffersResponse(
       ipc::AsyncResult<protos::gen::ReadBuffersResponse>);
   void OnEnableTracingResponse(
       ipc::AsyncResult<protos::gen::EnableTracingResponse>);
+  void OnQueryServiceStateResponse(
+      ipc::AsyncResult<protos::gen::QueryServiceStateResponse>,
+      PendingQueryServiceRequests::iterator);
 
   // TODO(primiano): think to dtor order, do we rely on any specific sequence?
   Consumer* const consumer_;
@@ -95,6 +111,8 @@
 
   bool connected_ = false;
 
+  PendingQueryServiceRequests pending_query_svc_reqs_;
+
   // When a packet is too big to fit into a ReadBuffersResponse IPC, the service
   // will chunk it into several IPCs, each containing few slices of the packet
   // (a packet's slice is always guaranteed to be << kIPCBufferSize). When
diff --git a/src/tracing/ipc/memfd.cc b/src/tracing/ipc/memfd.cc
new file mode 100644
index 0000000..64025bf
--- /dev/null
+++ b/src/tracing/ipc/memfd.cc
@@ -0,0 +1,95 @@
+/*
+ * 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/ipc/memfd.h"
+
+#include <errno.h>
+
+#define PERFETTO_MEMFD_ENABLED()             \
+  PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+      PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX)
+
+#if PERFETTO_MEMFD_ENABLED()
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/syscall.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+// Some android build bots use a sysroot that doesn't support memfd when
+// compiling for the host, so we redefine it if necessary.
+#if !defined(__NR_memfd_create)
+#if defined(__x86_64__)
+#define __NR_memfd_create 319
+#elif defined(__i386__)
+#define __NR_memfd_create 356
+#elif defined(__aarch64__)
+#define __NR_memfd_create 279
+#elif defined(__arm__)
+#define __NR_memfd_create 385
+#else
+#error "unsupported sysroot without memfd support"
+#endif
+#endif  // !defined(__NR_memfd_create)
+
+namespace perfetto {
+bool HasMemfdSupport() {
+  static bool kSupportsMemfd = [] {
+    // Check kernel version supports memfd_create(). Some older kernels segfault
+    // executing memfd_create() rather than returning ENOSYS (b/116769556).
+    static constexpr int kRequiredMajor = 3;
+    static constexpr int kRequiredMinor = 17;
+    struct utsname uts;
+    int major, minor;
+    if (uname(&uts) == 0 && strcmp(uts.sysname, "Linux") == 0 &&
+        sscanf(uts.release, "%d.%d", &major, &minor) == 2 &&
+        ((major < kRequiredMajor ||
+          (major == kRequiredMajor && minor < kRequiredMinor)))) {
+      return false;
+    }
+
+    base::ScopedFile fd;
+    fd.reset(static_cast<int>(syscall(__NR_memfd_create, "perfetto_shmem",
+                                      MFD_CLOEXEC | MFD_ALLOW_SEALING)));
+    return !!fd;
+  }();
+  return kSupportsMemfd;
+}
+
+base::ScopedFile CreateMemfd(const char* name, unsigned int flags) {
+  if (!HasMemfdSupport()) {
+    errno = ENOSYS;
+    return base::ScopedFile();
+  }
+  return base::ScopedFile(
+      static_cast<int>(syscall(__NR_memfd_create, name, flags)));
+}
+}  // namespace perfetto
+
+#else  // PERFETTO_MEMFD_ENABLED()
+
+namespace perfetto {
+bool HasMemfdSupport() {
+  return false;
+}
+base::ScopedFile CreateMemfd(const char*, unsigned int) {
+  errno = ENOSYS;
+  return base::ScopedFile();
+}
+}  // namespace perfetto
+
+#endif  // PERFETTO_MEMFD_ENABLED()
diff --git a/src/tracing/ipc/memfd.h b/src/tracing/ipc/memfd.h
new file mode 100644
index 0000000..8cf4b2a
--- /dev/null
+++ b/src/tracing/ipc/memfd.h
@@ -0,0 +1,55 @@
+/*
+ * 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_TRACING_IPC_MEMFD_H_
+#define SRC_TRACING_IPC_MEMFD_H_
+
+#include "perfetto/base/build_config.h"
+
+#include "perfetto/ext/base/scoped_file.h"
+
+// Some android build bots use a sysroot that doesn't support memfd when
+// compiling for the host, so we define the flags we need ourselves.
+
+// from memfd.h
+#ifndef MFD_CLOEXEC
+#define MFD_CLOEXEC 0x0001U
+#define MFD_ALLOW_SEALING 0x0002U
+#endif
+
+// from fcntl.h
+#ifndef F_ADD_SEALS
+#define F_ADD_SEALS 1033
+#define F_GET_SEALS 1034
+#define F_SEAL_SEAL 0x0001
+#define F_SEAL_SHRINK 0x0002
+#define F_SEAL_GROW 0x0004
+#define F_SEAL_WRITE 0x0008
+#endif
+
+namespace perfetto {
+
+// Whether the operating system supports memfd.
+bool HasMemfdSupport();
+
+// Call memfd(2) if available on platform and return the fd as result. This call
+// also makes a kernel version check for safety on older kernels (b/116769556).
+// Returns an invalid ScopedFile on failure.
+base::ScopedFile CreateMemfd(const char* name, unsigned int flags);
+
+}  // namespace perfetto
+
+#endif  // SRC_TRACING_IPC_MEMFD_H_
diff --git a/src/tracing/ipc/posix_shared_memory.cc b/src/tracing/ipc/posix_shared_memory.cc
index d7f0904..5fc7982 100644
--- a/src/tracing/ipc/posix_shared_memory.cc
+++ b/src/tracing/ipc/posix_shared_memory.cc
@@ -27,50 +27,71 @@
 #include <memory>
 #include <utility>
 
-#include "perfetto/base/build_config.h"
+#include "perfetto/base/compiler.h"
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/temp_file.h"
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#include <linux/memfd.h>
-#include <sys/syscall.h>
-#endif
+#include "src/tracing/ipc/memfd.h"
 
 namespace perfetto {
 
+namespace {
+int kFileSeals = F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_SEAL;
+}  // namespace
+
 // static
 std::unique_ptr<PosixSharedMemory> PosixSharedMemory::Create(size_t size) {
-  base::ScopedFile fd;
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-  bool is_memfd = false;
-  fd.reset(static_cast<int>(syscall(__NR_memfd_create, "perfetto_shmem",
-                                    MFD_CLOEXEC | MFD_ALLOW_SEALING)));
-  is_memfd = !!fd;
+  base::ScopedFile fd =
+      CreateMemfd("perfetto_shmem", MFD_CLOEXEC | MFD_ALLOW_SEALING);
+  bool is_memfd = !!fd;
 
+  // In-tree builds only allow mem_fd, so we can inspect the seals to verify the
+  // fd is appropriately sealed. We'll crash in the PERFETTO_CHECK(fd) below if
+  // memfd_create failed.
+#if !PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
   if (!fd) {
     // TODO: if this fails on Android we should fall back on ashmem.
     PERFETTO_DPLOG("memfd_create() failed");
+    fd = base::TempFile::CreateUnlinked().ReleaseFD();
   }
 #endif
 
-  if (!fd)
-    fd = base::TempFile::CreateUnlinked().ReleaseFD();
-
   PERFETTO_CHECK(fd);
   int res = ftruncate(fd.get(), static_cast<off_t>(size));
   PERFETTO_CHECK(res == 0);
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+
   if (is_memfd) {
-    res = fcntl(*fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_SEAL);
+    // When memfd is supported, file seals should be, too.
+    res = fcntl(*fd, F_ADD_SEALS, kFileSeals);
     PERFETTO_DCHECK(res == 0);
   }
-#endif
+
   return MapFD(std::move(fd), size);
 }
 
 // static
 std::unique_ptr<PosixSharedMemory> PosixSharedMemory::AttachToFd(
-    base::ScopedFile fd) {
+    base::ScopedFile fd,
+    bool require_seals_if_supported) {
+  bool requires_seals = require_seals_if_supported;
+
+#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+  // In-tree kernels all support memfd.
+  PERFETTO_CHECK(HasMemfdSupport());
+#else
+  // In out-of-tree builds, we only require seals if the kernel supports memfd.
+  if (requires_seals)
+    requires_seals = HasMemfdSupport();
+#endif
+
+  if (requires_seals) {
+    // If the system supports memfd, we require a sealed memfd.
+    int res = fcntl(*fd, F_GET_SEALS);
+    if (res == -1 || (res & kFileSeals) != kFileSeals) {
+      PERFETTO_PLOG("Couldn't verify file seals on shmem FD");
+      return nullptr;
+    }
+  }
+
   struct stat stat_buf = {};
   int res = fstat(fd.get(), &stat_buf);
   PERFETTO_CHECK(res == 0 && stat_buf.st_size > 0);
diff --git a/src/tracing/ipc/posix_shared_memory.h b/src/tracing/ipc/posix_shared_memory.h
index 4bb3baf..e637015 100644
--- a/src/tracing/ipc/posix_shared_memory.h
+++ b/src/tracing/ipc/posix_shared_memory.h
@@ -21,6 +21,7 @@
 
 #include <memory>
 
+#include "perfetto/base/build_config.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/tracing/core/shared_memory.h"
 
@@ -35,15 +36,21 @@
     std::unique_ptr<SharedMemory> CreateSharedMemory(size_t) override;
   };
 
-  // Create a brand new SHM region (the service uses this).
+  // Create a brand new SHM region.
   static std::unique_ptr<PosixSharedMemory> Create(size_t size);
 
-  // Mmaps a file descriptor to an existing SHM region (the producer uses this).
-  static std::unique_ptr<PosixSharedMemory> AttachToFd(base::ScopedFile);
+  // Mmaps a file descriptor to an existing SHM region. If
+  // |require_seals_if_supported| is true and the system supports
+  // memfd_create(), the FD is required to be a sealed memfd with F_SEAL_SEAL,
+  // F_SEAL_GROW, and F_SEAL_SHRINK seals set (otherwise, nullptr is returned).
+  // May also return nullptr if mapping fails for another reason (e.g. OOM).
+  static std::unique_ptr<PosixSharedMemory> AttachToFd(
+      base::ScopedFile,
+      bool require_seals_if_supported = true);
 
   ~PosixSharedMemory() override;
 
-  int fd() const { return fd_.get(); }
+  int fd() const override { return fd_.get(); }
 
   // SharedMemory implementation.
   void* start() const override { return start_; }
diff --git a/src/tracing/ipc/posix_shared_memory_unittest.cc b/src/tracing/ipc/posix_shared_memory_unittest.cc
index 1156556..e5589d0 100644
--- a/src/tracing/ipc/posix_shared_memory_unittest.cc
+++ b/src/tracing/ipc/posix_shared_memory_unittest.cc
@@ -30,6 +30,7 @@
 #include "perfetto/ext/base/utils.h"
 #include "src/base/test/test_task_runner.h"
 #include "src/base/test/vm_test_utils.h"
+#include "src/tracing/ipc/memfd.h"
 #include "test/gtest_and_gmock.h"
 
 namespace perfetto {
@@ -43,6 +44,7 @@
   PosixSharedMemory::Factory factory;
   std::unique_ptr<SharedMemory> shm =
       factory.CreateSharedMemory(base::kPageSize);
+  ASSERT_NE(shm.get(), nullptr);
   void* const shm_start = shm->start();
   const size_t shm_size = shm->size();
   ASSERT_NE(nullptr, shm_start);
@@ -58,6 +60,7 @@
 TEST(PosixSharedMemoryTest, DestructorClosesFD) {
   std::unique_ptr<PosixSharedMemory> shm =
       PosixSharedMemory::Create(base::kPageSize);
+  ASSERT_NE(shm.get(), nullptr);
   int fd = shm->fd();
   ASSERT_GE(fd, 0);
   ASSERT_EQ(static_cast<off_t>(base::kPageSize), lseek(fd, 0, SEEK_END));
@@ -66,14 +69,15 @@
   ASSERT_TRUE(IsFileDescriptorClosed(fd));
 }
 
-TEST(PosixSharedMemoryTest, AttachToFd) {
+TEST(PosixSharedMemoryTest, AttachToFdWithoutSeals) {
   base::TempFile tmp_file = base::TempFile::CreateUnlinked();
   const int fd_num = tmp_file.fd();
   ASSERT_EQ(0, ftruncate(fd_num, base::kPageSize));
   ASSERT_EQ(7, base::WriteAll(fd_num, "foobar", 7));
 
-  std::unique_ptr<PosixSharedMemory> shm =
-      PosixSharedMemory::AttachToFd(tmp_file.ReleaseFD());
+  std::unique_ptr<PosixSharedMemory> shm = PosixSharedMemory::AttachToFd(
+      tmp_file.ReleaseFD(), /*require_seals_if_supported=*/false);
+  ASSERT_NE(shm.get(), nullptr);
   void* const shm_start = shm->start();
   const size_t shm_size = shm->size();
   ASSERT_NE(nullptr, shm_start);
@@ -87,5 +91,53 @@
   ASSERT_FALSE(base::vm_test_utils::IsMapped(shm_start, shm_size));
 }
 
+TEST(PosixSharedMemoryTest, AttachToFdRequiresSeals) {
+  base::TempFile tmp_file = base::TempFile::CreateUnlinked();
+  const int fd_num = tmp_file.fd();
+  ASSERT_EQ(0, ftruncate(fd_num, base::kPageSize));
+
+  std::unique_ptr<PosixSharedMemory> shm =
+      PosixSharedMemory::AttachToFd(tmp_file.ReleaseFD());
+
+  if (HasMemfdSupport()) {
+    EXPECT_EQ(shm.get(), nullptr);
+  } else {
+    ASSERT_NE(shm.get(), nullptr);
+    EXPECT_NE(shm->start(), nullptr);
+  }
+}
+
+TEST(PosixSharedMemoryTest, CreateAndMap) {
+  std::unique_ptr<PosixSharedMemory> shm =
+      PosixSharedMemory::Create(base::kPageSize);
+  void* const shm_start = shm->start();
+  const size_t shm_size = shm->size();
+  ASSERT_NE(shm_start, nullptr);
+  ASSERT_EQ(shm_size, base::kPageSize);
+
+  memcpy(shm_start, "test", 5);
+  ASSERT_TRUE(base::vm_test_utils::IsMapped(shm_start, shm_size));
+
+  base::ScopedFile shm_fd2(dup(shm->fd()));
+  std::unique_ptr<PosixSharedMemory> shm2 =
+      PosixSharedMemory::AttachToFd(std::move(shm_fd2));
+  ASSERT_NE(shm2.get(), nullptr);
+  void* const shm2_start = shm2->start();
+  const size_t shm2_size = shm2->size();
+  ASSERT_NE(shm2_start, nullptr);
+  ASSERT_EQ(shm2_size, shm_size);
+
+  ASSERT_EQ(0, memcmp("test", shm2->start(), 5));
+  ASSERT_TRUE(base::vm_test_utils::IsMapped(shm2_start, shm2_size));
+
+  shm2.reset();
+  ASSERT_FALSE(base::vm_test_utils::IsMapped(shm2_start, shm2_size));
+  ASSERT_TRUE(base::vm_test_utils::IsMapped(shm_start, shm_size));
+
+  shm.reset();
+  ASSERT_FALSE(base::vm_test_utils::IsMapped(shm2_start, shm2_size));
+  ASSERT_FALSE(base::vm_test_utils::IsMapped(shm_start, shm_size));
+}
+
 }  // namespace
 }  // 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..58fc05d 100644
--- a/src/tracing/ipc/producer/producer_ipc_client_impl.cc
+++ b/src/tracing/ipc/producer/producer_ipc_client_impl.cc
@@ -45,12 +45,15 @@
     base::TaskRunner* task_runner,
     TracingService::ProducerSMBScrapingMode smb_scraping_mode,
     size_t shared_memory_size_hint_bytes,
-    size_t shared_memory_page_size_hint_bytes) {
+    size_t shared_memory_page_size_hint_bytes,
+    std::unique_ptr<SharedMemory> shm,
+    std::unique_ptr<SharedMemoryArbiter> shm_arbiter) {
   return std::unique_ptr<TracingService::ProducerEndpoint>(
       new ProducerIPCClientImpl(service_sock_name, producer, producer_name,
                                 task_runner, smb_scraping_mode,
                                 shared_memory_size_hint_bytes,
-                                shared_memory_page_size_hint_bytes));
+                                shared_memory_page_size_hint_bytes,
+                                std::move(shm), std::move(shm_arbiter)));
 }
 
 ProducerIPCClientImpl::ProducerIPCClientImpl(
@@ -60,15 +63,31 @@
     base::TaskRunner* task_runner,
     TracingService::ProducerSMBScrapingMode smb_scraping_mode,
     size_t shared_memory_size_hint_bytes,
-    size_t shared_memory_page_size_hint_bytes)
+    size_t shared_memory_page_size_hint_bytes,
+    std::unique_ptr<SharedMemory> shm,
+    std::unique_ptr<SharedMemoryArbiter> shm_arbiter)
     : producer_(producer),
       task_runner_(task_runner),
       ipc_channel_(ipc::Client::CreateInstance(service_sock_name, task_runner)),
       producer_port_(this /* event_listener */),
+      shared_memory_(std::move(shm)),
+      shared_memory_arbiter_(std::move(shm_arbiter)),
       name_(producer_name),
       shared_memory_page_size_hint_bytes_(shared_memory_page_size_hint_bytes),
       shared_memory_size_hint_bytes_(shared_memory_size_hint_bytes),
       smb_scraping_mode_(smb_scraping_mode) {
+  // Check for producer-provided SMB (used by Chrome for startup tracing).
+  if (shared_memory_) {
+    // We also expect a valid (unbound) arbiter. Bind it to this endpoint now.
+    PERFETTO_CHECK(shared_memory_arbiter_);
+    shared_memory_arbiter_->BindToProducerEndpoint(this, task_runner_);
+
+    // If the service accepts our SMB, then it must match our requested page
+    // layout. The protocol doesn't allow the service to change the size and
+    // layout when the SMB is provided by the producer.
+    shared_buffer_page_size_kb_ = shared_memory_page_size_hint_bytes_ / 1024;
+  }
+
   ipc_channel_->BindService(producer_port_.GetWeakPtr());
   PERFETTO_DCHECK_THREAD(thread_checker_);
 }
@@ -86,7 +105,9 @@
   ipc::Deferred<protos::gen::InitializeConnectionResponse> on_init;
   on_init.Bind(
       [this](ipc::AsyncResult<protos::gen::InitializeConnectionResponse> resp) {
-        OnConnectionInitialized(resp.success());
+        OnConnectionInitialized(
+            resp.success(),
+            resp.success() ? resp->using_shmem_provided_by_producer() : false);
       });
   protos::gen::InitializeConnectionRequest req;
   req.set_producer_name(name_);
@@ -109,6 +130,12 @@
       break;
   }
 
+  int shm_fd = -1;
+  if (shared_memory_) {
+    shm_fd = shared_memory_->fd();
+    req.set_producer_provided_shmem(true);
+  }
+
 #if PERFETTO_DCHECK_IS_ON()
   req.set_build_flags(
       protos::gen::InitializeConnectionRequest::BUILD_FLAGS_DCHECKS_ON);
@@ -116,7 +143,7 @@
   req.set_build_flags(
       protos::gen::InitializeConnectionRequest::BUILD_FLAGS_DCHECKS_OFF);
 #endif
-  producer_port_.InitializeConnection(req, std::move(on_init));
+  producer_port_.InitializeConnection(req, std::move(on_init), shm_fd);
 
   // Create the back channel to receive commands from the Service.
   ipc::Deferred<protos::gen::GetAsyncCommandResponse> on_cmd;
@@ -128,6 +155,11 @@
       });
   producer_port_.GetAsyncCommand(protos::gen::GetAsyncCommandRequest(),
                                  std::move(on_cmd));
+
+  // If there are pending Sync() requests, send them now.
+  for (const auto& pending_sync : pending_sync_reqs_)
+    Sync(std::move(pending_sync));
+  pending_sync_reqs_.clear();
 }
 
 void ProducerIPCClientImpl::OnDisconnect() {
@@ -138,13 +170,24 @@
   data_sources_setup_.clear();
 }
 
-void ProducerIPCClientImpl::OnConnectionInitialized(bool connection_succeeded) {
+void ProducerIPCClientImpl::OnConnectionInitialized(
+    bool connection_succeeded,
+    bool using_shmem_provided_by_producer) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
   // If connection_succeeded == false, the OnDisconnect() call will follow next
   // and there we'll notify the |producer_|. TODO: add a test for this.
   if (!connection_succeeded)
     return;
+  is_shmem_provided_by_producer_ = using_shmem_provided_by_producer;
   producer_->OnConnect();
+
+  // Bail out if the service failed to adopt our producer-allocated SMB.
+  // TODO(eseckler): Handle adoption failure more gracefully.
+  if (shared_memory_ && !is_shmem_provided_by_producer_) {
+    PERFETTO_DLOG("Service failed adopt producer-provided SMB, disconnecting.");
+    ipc_channel_.reset();
+    return;
+  }
 }
 
 void ProducerIPCClientImpl::OnServiceRequest(
@@ -183,15 +226,24 @@
 
   if (cmd.has_setup_tracing()) {
     base::ScopedFile shmem_fd = ipc_channel_->TakeReceivedFD();
-    PERFETTO_CHECK(shmem_fd);
-
-    // TODO(primiano): handle mmap failure in case of OOM.
-    shared_memory_ = PosixSharedMemory::AttachToFd(std::move(shmem_fd));
-    shared_buffer_page_size_kb_ =
-        cmd.setup_tracing().shared_buffer_page_size_kb();
-    shared_memory_arbiter_ = SharedMemoryArbiter::CreateInstance(
-        shared_memory_.get(), shared_buffer_page_size_kb_ * 1024, this,
-        task_runner_);
+    if (shmem_fd) {
+      // This is the nominal case used in most configurations, where the service
+      // provides the SMB.
+      PERFETTO_CHECK(!is_shmem_provided_by_producer_ && !shared_memory_);
+      // TODO(primiano): handle mmap failure in case of OOM.
+      shared_memory_ =
+          PosixSharedMemory::AttachToFd(std::move(shmem_fd),
+                                        /*require_seals_if_supported=*/false);
+      shared_buffer_page_size_kb_ =
+          cmd.setup_tracing().shared_buffer_page_size_kb();
+      shared_memory_arbiter_ = SharedMemoryArbiter::CreateInstance(
+          shared_memory_.get(), shared_buffer_page_size_kb_ * 1024, this,
+          task_runner_);
+    } else {
+      // Producer-provided SMB (used by Chrome for startup tracing).
+      PERFETTO_CHECK(is_shmem_provided_by_producer_ && shared_memory_ &&
+                     shared_memory_arbiter_);
+    }
     producer_->OnTracingSetup();
     return;
   }
@@ -349,6 +401,23 @@
       proto_req, ipc::Deferred<protos::gen::ActivateTriggersResponse>());
 }
 
+void ProducerIPCClientImpl::Sync(std::function<void()> callback) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  if (!connected_) {
+    pending_sync_reqs_.emplace_back(std::move(callback));
+    return;
+  }
+  ipc::Deferred<protos::gen::SyncResponse> resp;
+  resp.Bind([callback](ipc::AsyncResult<protos::gen::SyncResponse>) {
+    // Here we ACK the callback even if the service replies with a failure
+    // (i.e. the service is too old and doesn't understand Sync()). In that
+    // case the service has still seen the request, the IPC roundtrip is
+    // still a (weaker) linearization fence.
+    callback();
+  });
+  producer_port_.Sync(protos::gen::SyncRequest(), std::move(resp));
+}
+
 std::unique_ptr<TraceWriter> ProducerIPCClientImpl::CreateTraceWriter(
     BufferID target_buffer,
     BufferExhaustedPolicy buffer_exhausted_policy) {
@@ -358,9 +427,12 @@
                                                    buffer_exhausted_policy);
 }
 
-SharedMemoryArbiter* ProducerIPCClientImpl::GetInProcessShmemArbiter() {
-  PERFETTO_DLOG("Cannot GetInProcessShmemArbiter() via the IPC layer.");
-  return nullptr;
+SharedMemoryArbiter* ProducerIPCClientImpl::MaybeSharedMemoryArbiter() {
+  return shared_memory_arbiter_.get();
+}
+
+bool ProducerIPCClientImpl::IsShmemProvidedByProducer() const {
+  return is_shmem_provided_by_producer_;
 }
 
 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..2995d38 100644
--- a/src/tracing/ipc/producer/producer_ipc_client_impl.h
+++ b/src/tracing/ipc/producer/producer_ipc_client_impl.h
@@ -42,7 +42,6 @@
 }  // namespace ipc
 
 class Producer;
-class PosixSharedMemory;
 class SharedMemoryArbiter;
 
 // Exposes a Service endpoint to Producer(s), proxying all requests through a
@@ -52,13 +51,16 @@
 class ProducerIPCClientImpl : public TracingService::ProducerEndpoint,
                               public ipc::ServiceProxy::EventListener {
  public:
-  ProducerIPCClientImpl(const char* service_sock_name,
-                        Producer*,
-                        const std::string& producer_name,
-                        base::TaskRunner*,
-                        TracingService::ProducerSMBScrapingMode,
-                        size_t shared_memory_size_hint_bytes = 0,
-                        size_t shared_memory_page_size_hint_bytes = 0);
+  ProducerIPCClientImpl(
+      const char* service_sock_name,
+      Producer*,
+      const std::string& producer_name,
+      base::TaskRunner*,
+      TracingService::ProducerSMBScrapingMode,
+      size_t shared_memory_size_hint_bytes = 0,
+      size_t shared_memory_page_size_hint_bytes = 0,
+      std::unique_ptr<SharedMemory> shm = nullptr,
+      std::unique_ptr<SharedMemoryArbiter> shm_arbiter = nullptr);
   ~ProducerIPCClientImpl() override;
 
   // TracingService::ProducerEndpoint implementation.
@@ -72,11 +74,13 @@
   void NotifyDataSourceStarted(DataSourceInstanceID) override;
   void NotifyDataSourceStopped(DataSourceInstanceID) override;
   void ActivateTriggers(const std::vector<std::string>&) override;
+  void Sync(std::function<void()> callback) override;
 
   std::unique_ptr<TraceWriter> CreateTraceWriter(
       BufferID target_buffer,
       BufferExhaustedPolicy) override;
-  SharedMemoryArbiter* GetInProcessShmemArbiter() override;
+  SharedMemoryArbiter* MaybeSharedMemoryArbiter() override;
+  bool IsShmemProvidedByProducer() const override;
   void NotifyFlushComplete(FlushRequestID) override;
   SharedMemory* shared_memory() const override;
   size_t shared_buffer_page_size_kb() const override;
@@ -89,7 +93,8 @@
 
  private:
   // Invoked soon after having established the connection with the service.
-  void OnConnectionInitialized(bool connection_succeeded);
+  void OnConnectionInitialized(bool connection_succeeded,
+                               bool using_shmem_provided_by_producer);
 
   // Invoked when the remote Service sends an IPC to tell us to do something
   // (e.g. start/stop a data source).
@@ -106,7 +111,7 @@
   // to |ipc_channel_| and (de)serializes method invocations over the wire.
   protos::gen::ProducerPortProxy producer_port_;
 
-  std::unique_ptr<PosixSharedMemory> shared_memory_;
+  std::unique_ptr<SharedMemory> shared_memory_;
   std::unique_ptr<SharedMemoryArbiter> shared_memory_arbiter_;
   size_t shared_buffer_page_size_kb_ = 0;
   std::set<DataSourceInstanceID> data_sources_setup_;
@@ -115,6 +120,8 @@
   size_t shared_memory_page_size_hint_bytes_ = 0;
   size_t shared_memory_size_hint_bytes_ = 0;
   TracingService::ProducerSMBScrapingMode const smb_scraping_mode_;
+  bool is_shmem_provided_by_producer_ = false;
+  std::vector<std::function<void()>> pending_sync_reqs_;
   PERFETTO_THREAD_CHECKER(thread_checker_)
 };
 
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/src/tracing/ipc/service/consumer_ipc_service.cc b/src/tracing/ipc/service/consumer_ipc_service.cc
index 0f8d64d..073bc74 100644
--- a/src/tracing/ipc/service/consumer_ipc_service.cc
+++ b/src/tracing/ipc/service/consumer_ipc_service.cc
@@ -29,6 +29,7 @@
 #include "perfetto/ext/tracing/core/trace_stats.h"
 #include "perfetto/ext/tracing/core/tracing_service.h"
 #include "perfetto/tracing/core/trace_config.h"
+#include "perfetto/tracing/core/tracing_service_capabilities.h"
 #include "perfetto/tracing/core/tracing_service_state.h"
 
 namespace perfetto {
@@ -71,7 +72,7 @@
   }
   const TraceConfig& trace_config = req.trace_config();
   base::ScopedFile fd;
-  if (trace_config.write_into_file())
+  if (trace_config.write_into_file() && trace_config.output_path().empty())
     fd = ipc::Service::TakeReceivedFD();
   remote_consumer->service_endpoint->EnableTracing(trace_config, std::move(fd));
   remote_consumer->enable_tracing_response = std::move(resp);
@@ -170,22 +171,15 @@
 
   remote_consumer->observe_events_response = std::move(resp);
 
-  bool observe_instances = false;
+  uint32_t events_mask = 0;
   for (const auto& type : req.events_to_observe()) {
-    switch (static_cast<int>(type)) {
-      case protos::gen::ObservableEvents::TYPE_DATA_SOURCES_INSTANCES:
-        observe_instances = true;
-        break;
-      default:
-        PERFETTO_DFATAL("Unknown ObservableEvent type: %d", type);
-        break;
-    }
+    events_mask |= static_cast<uint32_t>(type);
   }
-  remote_consumer->service_endpoint->ObserveEvents(observe_instances);
+  remote_consumer->service_endpoint->ObserveEvents(events_mask);
 
   // If no events are to be observed, close the stream immediately so that the
   // client can clean up.
-  if (req.events_to_observe().size() == 0)
+  if (events_mask == 0)
     remote_consumer->CloseObserveEventsResponseStream();
 }
 
@@ -205,6 +199,76 @@
   remote_consumer->service_endpoint->QueryServiceState(callback);
 }
 
+// Called by the service in response to service_endpoint->QueryServiceState().
+void ConsumerIPCService::OnQueryServiceCallback(
+    bool success,
+    const TracingServiceState& svc_state,
+    PendingQuerySvcResponses::iterator pending_response_it) {
+  DeferredQueryServiceStateResponse response(std::move(*pending_response_it));
+  pending_query_service_responses_.erase(pending_response_it);
+  if (!success) {
+    response.Reject();
+    return;
+  }
+
+  // The TracingServiceState object might be too big to fit into a single IPC
+  // message because it contains the DataSourceDescriptor of each data source.
+  // Here we split it in chunks to fit in the IPC limit, observing the
+  // following rule: each chunk must be invididually a valid TracingServiceState
+  // message; all the chunks concatenated together must form the original
+  // message. This is to deal with the legacy API that was just sending one
+  // whole message (failing in presence of too many data sources, b/153142114).
+  // The message is split as follows: we take the whole TracingServiceState,
+  // take out the data sources section (which is a top-level repeated field)
+  // and re-add them one-by-one. If, in the process of appending, the IPC msg
+  // size is reached, a new chunk is created. This assumes that the rest of
+  // TracingServiceState fits in one IPC message and each DataSourceDescriptor
+  // fits in the worst case in a dedicated message (which is true, because
+  // otherwise the RegisterDataSource() which passes the descriptor in the first
+  // place would fail).
+
+  std::vector<uint8_t> chunked_reply;
+
+  // Transmits the current chunk and starts a new one.
+  bool sent_eof = false;
+  auto send_chunked_reply = [&chunked_reply, &response,
+                             &sent_eof](bool has_more) {
+    PERFETTO_CHECK(!sent_eof);
+    sent_eof = !has_more;
+    auto resp =
+        ipc::AsyncResult<protos::gen::QueryServiceStateResponse>::Create();
+    resp.set_has_more(has_more);
+    PERFETTO_CHECK(resp->mutable_service_state()->ParseFromArray(
+        chunked_reply.data(), chunked_reply.size()));
+    chunked_reply.clear();
+    response.Resolve(std::move(resp));
+  };
+
+  // Create a copy of the whole response and cut away the data_sources section.
+  protos::gen::TracingServiceState svc_state_copy = svc_state;
+  auto data_sources = std::move(*svc_state_copy.mutable_data_sources());
+  chunked_reply = svc_state_copy.SerializeAsArray();
+
+  // Now re-add them fitting within the IPC message limits (- some margin for
+  // the outer IPC frame).
+  constexpr size_t kMaxMsgSize = ipc::kIPCBufferSize - 128;
+  for (const auto& data_source : data_sources) {
+    protos::gen::TracingServiceState tmp;
+    tmp.mutable_data_sources()->emplace_back(std::move(data_source));
+    std::vector<uint8_t> chunk = tmp.SerializeAsArray();
+    if (chunked_reply.size() + chunk.size() < kMaxMsgSize) {
+      chunked_reply.insert(chunked_reply.end(), chunk.begin(), chunk.end());
+    } else {
+      send_chunked_reply(/*has_more=*/true);
+      chunked_reply = std::move(chunk);
+    }
+  }
+
+  PERFETTO_DCHECK(!chunked_reply.empty());
+  send_chunked_reply(/*has_more=*/false);
+  PERFETTO_CHECK(sent_eof);
+}
+
 // Called by the service in response to a service_endpoint->Flush() request.
 void ConsumerIPCService::OnFlushCallback(
     bool success,
@@ -218,21 +282,30 @@
   }
 }
 
-// Called by the service in response to service_endpoint->QueryServiceState().
-void ConsumerIPCService::OnQueryServiceCallback(
-    bool success,
-    const TracingServiceState& svc_state,
-    PendingQuerySvcResponses::iterator pending_response_it) {
-  DeferredQueryServiceStateResponse response(std::move(*pending_response_it));
-  pending_query_service_responses_.erase(pending_response_it);
-  if (success) {
-    auto resp =
-        ipc::AsyncResult<protos::gen::QueryServiceStateResponse>::Create();
-    *resp->mutable_service_state() = svc_state;
-    response.Resolve(std::move(resp));
-  } else {
-    response.Reject();
-  }
+void ConsumerIPCService::QueryCapabilities(
+    const protos::gen::QueryCapabilitiesRequest&,
+    DeferredQueryCapabilitiesResponse resp) {
+  RemoteConsumer* remote_consumer = GetConsumerForCurrentRequest();
+  auto it = pending_query_capabilities_responses_.insert(
+      pending_query_capabilities_responses_.end(), std::move(resp));
+  auto weak_this = weak_ptr_factory_.GetWeakPtr();
+  auto callback = [weak_this, it](const TracingServiceCapabilities& caps) {
+    if (weak_this)
+      weak_this->OnQueryCapabilitiesCallback(caps, std::move(it));
+  };
+  remote_consumer->service_endpoint->QueryCapabilities(callback);
+}
+
+// Called by the service in response to service_endpoint->QueryCapabilities().
+void ConsumerIPCService::OnQueryCapabilitiesCallback(
+    const TracingServiceCapabilities& caps,
+    PendingQueryCapabilitiesResponses::iterator pending_response_it) {
+  DeferredQueryCapabilitiesResponse response(std::move(*pending_response_it));
+  pending_query_capabilities_responses_.erase(pending_response_it);
+  auto resp =
+      ipc::AsyncResult<protos::gen::QueryCapabilitiesResponse>::Create();
+  *resp->mutable_capabilities() = caps;
+  response.Resolve(std::move(resp));
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/tracing/ipc/service/consumer_ipc_service.h b/src/tracing/ipc/service/consumer_ipc_service.h
index 006f5ca..d513371 100644
--- a/src/tracing/ipc/service/consumer_ipc_service.h
+++ b/src/tracing/ipc/service/consumer_ipc_service.h
@@ -67,6 +67,8 @@
                      DeferredObserveEventsResponse) override;
   void QueryServiceState(const protos::gen::QueryServiceStateRequest&,
                          DeferredQueryServiceStateResponse) override;
+  void QueryCapabilities(const protos::gen::QueryCapabilitiesRequest&,
+                         DeferredQueryCapabilitiesResponse) override;
   void OnClientDisconnected() override;
 
  private:
@@ -122,6 +124,8 @@
   // This has to be a container that doesn't invalidate iterators.
   using PendingFlushResponses = std::list<DeferredFlushResponse>;
   using PendingQuerySvcResponses = std::list<DeferredQueryServiceStateResponse>;
+  using PendingQueryCapabilitiesResponses =
+      std::list<DeferredQueryCapabilitiesResponse>;
 
   ConsumerIPCService(const ConsumerIPCService&) = delete;
   ConsumerIPCService& operator=(const ConsumerIPCService&) = delete;
@@ -134,6 +138,8 @@
   void OnQueryServiceCallback(bool success,
                               const TracingServiceState&,
                               PendingQuerySvcResponses::iterator);
+  void OnQueryCapabilitiesCallback(const TracingServiceCapabilities&,
+                                   PendingQueryCapabilitiesResponses::iterator);
 
   TracingService* const core_service_;
 
@@ -143,6 +149,7 @@
 
   PendingFlushResponses pending_flush_responses_;
   PendingQuerySvcResponses pending_query_service_responses_;
+  PendingQueryCapabilitiesResponses pending_query_capabilities_responses_;
 
   base::WeakPtrFactory<ConsumerIPCService> weak_ptr_factory_;  // Keep last.
 };
diff --git a/src/tracing/ipc/service/producer_ipc_service.cc b/src/tracing/ipc/service/producer_ipc_service.cc
index cfb4f43..bd79f4f 100644
--- a/src/tracing/ipc/service/producer_ipc_service.cc
+++ b/src/tracing/ipc/service/producer_ipc_service.cc
@@ -21,6 +21,7 @@
 #include "perfetto/base/logging.h"
 #include "perfetto/base/task_runner.h"
 #include "perfetto/ext/ipc/host.h"
+#include "perfetto/ext/ipc/service.h"
 #include "perfetto/ext/tracing/core/commit_data_request.h"
 #include "perfetto/ext/tracing/core/tracing_service.h"
 #include "perfetto/tracing/core/data_source_config.h"
@@ -94,12 +95,31 @@
         "DEBUG/NDEBUG flags. This will likely cause crashes.");
   }
 
+  // If the producer provided an SMB, tell the service to attempt to adopt it.
+  std::unique_ptr<SharedMemory> shmem;
+  if (req.producer_provided_shmem()) {
+    base::ScopedFile shmem_fd = ipc::Service::TakeReceivedFD();
+    if (shmem_fd) {
+      shmem = PosixSharedMemory::AttachToFd(
+          std::move(shmem_fd), /*require_seals_if_supported=*/true);
+      if (!shmem) {
+        PERFETTO_ELOG(
+            "Couldn't map producer-provided SMB, falling back to "
+            "service-provided SMB");
+      }
+    } else {
+      PERFETTO_DLOG(
+          "InitializeConnectionRequest's producer_provided_shmem flag is set "
+          "but the producer didn't provide an FD");
+    }
+  }
+
   // ConnectProducer will call OnConnect() on the next task.
   producer->service_endpoint = core_service_->ConnectProducer(
       producer.get(), client_info.uid(), req.producer_name(),
       req.shared_memory_size_hint_bytes(),
       /*in_process=*/false, smb_scraping_mode,
-      req.shared_memory_page_size_hint_bytes());
+      req.shared_memory_page_size_hint_bytes(), std::move(shmem));
 
   // Could happen if the service has too many producers connected.
   if (!producer->service_endpoint) {
@@ -107,11 +127,15 @@
     return;
   }
 
+  bool using_producer_shmem =
+      producer->service_endpoint->IsShmemProvidedByProducer();
+
   producers_.emplace(ipc_client_id, std::move(producer));
   // Because of the std::move() |producer| is invalid after this point.
 
   auto async_res =
       ipc::AsyncResult<protos::gen::InitializeConnectionResponse>::Create();
+  async_res->set_using_shmem_provided_by_producer(using_producer_shmem);
   response.Resolve(std::move(async_res));
 }
 
@@ -324,6 +348,30 @@
   // to send async commands to the RemoteProducer (e.g., starting/stopping a
   // data source).
   producer->async_producer_commands = std::move(response);
+
+  // Service may already have issued the OnTracingSetup() event, in which case
+  // we should forward it to the producer now.
+  if (producer->send_setup_tracing_on_async_commands_bound)
+    producer->SendSetupTracing();
+}
+
+void ProducerIPCService::Sync(const protos::gen::SyncRequest&,
+                              DeferredSyncResponse resp) {
+  RemoteProducer* producer = GetProducerForCurrentRequest();
+  if (!producer) {
+    PERFETTO_DLOG("Producer invoked Sync() before InitializeConnection()");
+    return resp.Reject();
+  }
+  auto weak_this = weak_ptr_factory_.GetWeakPtr();
+  auto resp_it = pending_syncs_.insert(pending_syncs_.end(), std::move(resp));
+  auto callback = [weak_this, resp_it]() {
+    if (!weak_this)
+      return;
+    auto pending_resp = std::move(*resp_it);
+    weak_this->pending_syncs_.erase(resp_it);
+    pending_resp.Resolve(ipc::AsyncResult<protos::gen::SyncResponse>::Create());
+  };
+  producer->service_endpoint->Sync(callback);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -394,19 +442,28 @@
 
 void ProducerIPCService::RemoteProducer::OnTracingSetup() {
   if (!async_producer_commands.IsBound()) {
-    PERFETTO_DLOG(
-        "The Service tried to allocate the shared memory but the remote "
-        "Producer has not yet initialized the connection");
+    // Service may call this before the producer issued GetAsyncCommand.
+    send_setup_tracing_on_async_commands_bound = true;
     return;
   }
+  SendSetupTracing();
+}
+
+void ProducerIPCService::RemoteProducer::SendSetupTracing() {
+  PERFETTO_CHECK(async_producer_commands.IsBound());
   PERFETTO_CHECK(service_endpoint->shared_memory());
-  const int shm_fd =
-      static_cast<PosixSharedMemory*>(service_endpoint->shared_memory())->fd();
   auto cmd = ipc::AsyncResult<protos::gen::GetAsyncCommandResponse>::Create();
   cmd.set_has_more(true);
-  cmd.set_fd(shm_fd);
-  cmd->mutable_setup_tracing()->set_shared_buffer_page_size_kb(
-      static_cast<uint32_t>(service_endpoint->shared_buffer_page_size_kb()));
+  auto setup_tracing = cmd->mutable_setup_tracing();
+  if (!service_endpoint->IsShmemProvidedByProducer()) {
+    // Nominal case (% Chrome): service provides SMB.
+    setup_tracing->set_shared_buffer_page_size_kb(
+        static_cast<uint32_t>(service_endpoint->shared_buffer_page_size_kb()));
+    const int shm_fd =
+        static_cast<PosixSharedMemory*>(service_endpoint->shared_memory())
+            ->fd();
+    cmd.set_fd(shm_fd);
+  }
   async_producer_commands.Resolve(std::move(cmd));
 }
 
diff --git a/src/tracing/ipc/service/producer_ipc_service.h b/src/tracing/ipc/service/producer_ipc_service.h
index 414c1ab..f22f186 100644
--- a/src/tracing/ipc/service/producer_ipc_service.h
+++ b/src/tracing/ipc/service/producer_ipc_service.h
@@ -17,6 +17,7 @@
 #ifndef SRC_TRACING_IPC_SERVICE_PRODUCER_IPC_SERVICE_H_
 #define SRC_TRACING_IPC_SERVICE_PRODUCER_IPC_SERVICE_H_
 
+#include <list>
 #include <map>
 #include <memory>
 #include <string>
@@ -61,12 +62,12 @@
   void NotifyDataSourceStopped(
       const protos::gen::NotifyDataSourceStoppedRequest&,
       DeferredNotifyDataSourceStoppedResponse) override;
-
   void ActivateTriggers(const protos::gen::ActivateTriggersRequest&,
                         DeferredActivateTriggersResponse) override;
 
   void GetAsyncCommand(const protos::gen::GetAsyncCommandRequest&,
                        DeferredGetAsyncCommandResponse) override;
+  void Sync(const protos::gen::SyncRequest&, DeferredSyncResponse) override;
   void OnClientDisconnected() override;
 
  private:
@@ -95,6 +96,8 @@
     void ClearIncrementalState(const DataSourceInstanceID* data_source_ids,
                                size_t num_data_sources) override;
 
+    void SendSetupTracing();
+
     // The interface obtained from the core service business logic through
     // Service::ConnectProducer(this). This allows to invoke methods for a
     // specific Producer on the Service business logic.
@@ -104,6 +107,11 @@
     // to send asynchronous commands to the remote Producer (e.g. start/stop a
     // data source).
     DeferredGetAsyncCommandResponse async_producer_commands;
+
+    // Set if the service calls OnTracingSetup() before the
+    // |async_producer_commands| was bound by the service. In this case, we
+    // forward the SetupTracing command when it is bound later.
+    bool send_setup_tracing_on_async_commands_bound = false;
   };
 
   ProducerIPCService(const ProducerIPCService&) = delete;
@@ -119,6 +127,9 @@
   // |core_service_| business logic.
   std::map<ipc::ClientID, std::unique_ptr<RemoteProducer>> producers_;
 
+  // List because pointers need to be stable.
+  std::list<DeferredSyncResponse> pending_syncs_;
+
   base::WeakPtrFactory<ProducerIPCService> weak_ptr_factory_;  // Keep last.
 };
 
diff --git a/src/tracing/test/BUILD.gn b/src/tracing/test/BUILD.gn
index 2c99bc9..fd9eed9 100644
--- a/src/tracing/test/BUILD.gn
+++ b/src/tracing/test/BUILD.gn
@@ -12,6 +12,96 @@
 # 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/common:cpp",
+      "../../../protos/perfetto/config/track_event:cpp",
+      "../../../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 69%
rename from src/tracing/api_integrationtest.cc
rename to src/tracing/test/api_integrationtest.cc
index c4edec1..6eaa39c 100644
--- a/src/tracing/api_integrationtest.cc
+++ b/src/tracing/test/api_integrationtest.cc
@@ -45,6 +45,8 @@
 // production code).
 // yyy.gen.h includes are for the test readback path (the code in the test that
 // checks that the results are valid).
+#include "protos/perfetto/common/track_event_descriptor.gen.h"
+#include "protos/perfetto/config/track_event/track_event_config.gen.h"
 #include "protos/perfetto/trace/clock_snapshot.pbzero.h"
 #include "protos/perfetto/trace/interned_data/interned_data.gen.h"
 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
@@ -69,10 +71,24 @@
 #include "protos/perfetto/trace/track_event/track_descriptor.gen.h"
 #include "protos/perfetto/trace/track_event/track_event.gen.h"
 
+// Events in categories starting with "dynamic" will use dynamic category
+// lookup.
+PERFETTO_DEFINE_TEST_CATEGORY_PREFIXES("dynamic");
+
 // Trace categories used in the tests.
-PERFETTO_DEFINE_CATEGORIES(PERFETTO_CATEGORY(test),
-                           PERFETTO_CATEGORY(foo),
-                           PERFETTO_CATEGORY(bar));
+PERFETTO_DEFINE_CATEGORIES(
+    perfetto::Category("test")
+        .SetDescription("This is a test category")
+        .SetTags("tag"),
+    perfetto::Category("foo"),
+    perfetto::Category("bar"),
+    perfetto::Category("cat").SetTags("slow"),
+    perfetto::Category("cat.verbose").SetTags("debug"),
+    perfetto::Category::Group("foo,bar"),
+    perfetto::Category::Group("baz,bar,quux"),
+    perfetto::Category::Group("red,green,blue,foo"),
+    perfetto::Category::Group("red,green,blue,yellow"),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("cat")));
 PERFETTO_TRACK_EVENT_STATIC_STORAGE();
 
 // For testing interning of complex objects.
@@ -92,6 +108,54 @@
 };
 }  // namespace std
 
+// Represents an opaque (from Perfetto's point of view) thread identifier (e.g.,
+// base::PlatformThreadId in Chromium).
+struct MyThreadId {
+  MyThreadId(int pid_, int tid_) : pid(pid_), tid(tid_) {}
+
+  const int pid = 0;
+  const int tid = 0;
+};
+
+// Represents an opaque timestamp (e.g., base::TimeTicks in Chromium).
+class MyTimestamp {
+ public:
+  explicit MyTimestamp(uint64_t ts_) : ts(ts_) {}
+
+  const uint64_t ts;
+};
+
+namespace perfetto {
+namespace legacy {
+
+template <>
+bool ConvertThreadId(const MyThreadId& thread,
+                     uint64_t* track_uuid_out,
+                     int32_t* pid_override_out,
+                     int32_t* tid_override_out) {
+  if (!thread.pid && !thread.tid)
+    return false;
+  if (!thread.pid) {
+    // Thread in current process.
+    *track_uuid_out = perfetto::ThreadTrack::ForThread(
+                          static_cast<base::PlatformThreadId>(thread.tid))
+                          .uuid;
+  } else {
+    // Thread in another process.
+    *pid_override_out = thread.pid;
+    *tid_override_out = thread.tid;
+  }
+  return true;
+}
+
+template <>
+uint64_t ConvertTimestampToTraceTimeNs(const MyTimestamp& timestamp) {
+  return timestamp.ts;
+}
+
+}  // namespace legacy
+}  // namespace perfetto
+
 namespace {
 
 using ::testing::_;
@@ -237,6 +301,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
 // -------------------------
@@ -280,6 +354,23 @@
     return handle;
   }
 
+  TestTracingSessionHandle* NewTraceWithCategories(
+      std::vector<std::string> categories) {
+    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");
+
+    perfetto::protos::gen::TrackEventConfig te_cfg;
+    te_cfg.add_disabled_categories("*");
+    for (const auto& category : categories)
+      te_cfg.add_enabled_categories(category);
+    ds_cfg->set_track_event_config_raw(te_cfg.SerializeAsString());
+
+    return NewTrace(cfg);
+  }
+
   std::vector<std::string> ReadLogMessagesFromTrace(
       perfetto::TracingSession* tracing_session) {
     std::vector<char> raw_trace = tracing_session->ReadTraceBlocking();
@@ -398,12 +489,51 @@
         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;
+        }
+        case perfetto::protos::gen::TrackEvent::TYPE_COUNTER:
+          slice += "C";
+          break;
+        default:
+          ADD_FAILURE();
       }
-      if (!track_event.category_iids().empty())
-        slice += ":" + categories[track_event.category_iids()[0]];
+      if (track_event.has_legacy_event()) {
+        auto legacy_event = track_event.legacy_event();
+        std::stringstream id;
+        if (legacy_event.has_unscoped_id()) {
+          id << "(unscoped_id=" << legacy_event.unscoped_id() << ")";
+        } else if (legacy_event.has_local_id()) {
+          id << "(local_id=" << legacy_event.local_id() << ")";
+        } else if (legacy_event.has_global_id()) {
+          id << "(global_id=" << legacy_event.global_id() << ")";
+        } else if (legacy_event.has_bind_id()) {
+          id << "(bind_id=" << legacy_event.bind_id() << ")";
+        }
+        if (legacy_event.has_id_scope())
+          id << "(id_scope=\"" << legacy_event.id_scope() << "\")";
+        if (legacy_event.use_async_tts())
+          id << "(use_async_tts)";
+        if (legacy_event.bind_to_enclosing())
+          id << "(bind_to_enclosing)";
+        if (legacy_event.has_flow_direction())
+          id << "(flow_direction=" << legacy_event.flow_direction() << ")";
+        if (legacy_event.has_pid_override())
+          id << "(pid_override=" << legacy_event.pid_override() << ")";
+        if (legacy_event.has_tid_override())
+          id << "(tid_override=" << legacy_event.tid_override() << ")";
+        slice += id.str();
+      }
+      size_t category_count = 0;
+      for (const auto& it : track_event.category_iids())
+        slice += (category_count++ ? "," : ":") + categories[it];
+      for (const auto& it : track_event.categories())
+        slice += (category_count++ ? ",$" : ":$") + it;
       if (track_event.has_name_iid())
         slice += "." + event_names[track_event.name_iid()];
 
@@ -497,7 +627,7 @@
   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("test");
+
   // Create five new trace sessions.
   std::vector<std::unique_ptr<perfetto::TracingSession>> sessions;
   for (size_t i = 0; i < 5; ++i) {
@@ -520,7 +650,7 @@
   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("test");
+
   // Create five new trace sessions.
   std::vector<std::unique_ptr<perfetto::TracingSession>> sessions;
   for (size_t i = 0; i < 5; ++i) {
@@ -535,17 +665,16 @@
   }
 }
 
-TEST_F(PerfettoApiTest, TrackEvent) {
-  // 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("test");
+// This is a build-only regression test that checks you can have a track event
+// inside a template.
+template <typename T>
+void TestTrackEventInsideTemplate(T) {
+  TRACE_EVENT_BEGIN("cat", "Name");
+}
 
+TEST_F(PerfettoApiTest, TrackEvent) {
   // Create a new trace session.
-  auto* tracing_session = NewTrace(cfg);
+  auto* tracing_session = NewTraceWithCategories({"test"});
   tracing_session->get()->StartBlocking();
 
   // Emit one complete track event.
@@ -563,11 +692,19 @@
   std::map<uint64_t, std::string> event_names;
   ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), raw_trace.size()));
 
+  auto now = perfetto::TrackEvent::GetTraceTimeNs();
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX) && \
+    !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  auto clock_id = perfetto::protos::pbzero::ClockSnapshot::Clock::BOOTTIME;
+#else
+  auto clock_id = perfetto::protos::pbzero::ClockSnapshot::Clock::MONOTONIC;
+#endif
+  EXPECT_EQ(clock_id, perfetto::TrackEvent::GetTraceClockId());
+
   bool incremental_state_was_cleared = false;
   bool begin_found = false;
   bool end_found = false;
   bool process_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()) {
@@ -624,7 +761,8 @@
 #else
     constexpr auto kClockMonotonic =
         perfetto::protos::pbzero::ClockSnapshot::Clock::MONOTONIC;
-    EXPECT_EQ(packet.timestamp_clock_id(), kClockMonotonic);
+    EXPECT_EQ(packet.timestamp_clock_id(),
+              static_cast<uint32_t>(kClockMonotonic));
 #endif
     if (track_event.type() ==
         perfetto::protos::gen::TrackEvent::TYPE_SLICE_BEGIN) {
@@ -646,19 +784,14 @@
   EXPECT_TRUE(process_descriptor_found);
   EXPECT_TRUE(begin_found);
   EXPECT_TRUE(end_found);
+
+  // Dummy instantiation of test template.
+  TestTrackEventInsideTemplate(true);
 }
 
 TEST_F(PerfettoApiTest, TrackEventCategories) {
-  // 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);
+  auto* tracing_session = NewTraceWithCategories({"bar"});
   tracing_session->get()->StartBlocking();
 
   // Emit some track events.
@@ -693,6 +826,34 @@
             muxer.data_sources[1].static_state);
 }
 
+TEST_F(PerfettoApiTest, TrackEventDescriptor) {
+  MockTracingMuxer muxer;
+
+  perfetto::TrackEvent::Register();
+  EXPECT_EQ(1u, muxer.data_sources.size());
+  EXPECT_EQ("track_event", muxer.data_sources[0].dsd.name());
+
+  perfetto::protos::gen::TrackEventDescriptor desc;
+  auto desc_raw = muxer.data_sources[0].dsd.track_event_descriptor_raw();
+  EXPECT_TRUE(desc.ParseFromArray(desc_raw.data(), desc_raw.size()));
+
+  // Check that the advertised categories match PERFETTO_DEFINE_CATEGORIES (see
+  // above).
+  EXPECT_EQ(6, desc.available_categories_size());
+  EXPECT_EQ("test", desc.available_categories()[0].name());
+  EXPECT_EQ("This is a test category",
+            desc.available_categories()[0].description());
+  EXPECT_EQ("tag", desc.available_categories()[0].tags()[0]);
+  EXPECT_EQ("foo", desc.available_categories()[1].name());
+  EXPECT_EQ("bar", desc.available_categories()[2].name());
+  EXPECT_EQ("cat", desc.available_categories()[3].name());
+  EXPECT_EQ("slow", desc.available_categories()[3].tags()[0]);
+  EXPECT_EQ("cat.verbose", desc.available_categories()[4].name());
+  EXPECT_EQ("debug", desc.available_categories()[4].tags()[0]);
+  EXPECT_EQ("disabled-by-default-cat", desc.available_categories()[5].name());
+  EXPECT_EQ("slow", desc.available_categories()[5].tags()[0]);
+}
+
 TEST_F(PerfettoApiTest, TrackEventSharedIncrementalState) {
   tracing_module::InitializeCategories();
 
@@ -725,19 +886,9 @@
   // enabled and disabled correctly.
   tracing_module::InitializeCategories();
 
-  // 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");
-
-  // Only the "foo" category is enabled. It also exists both locally and in the
-  // existing module.
-  ds_cfg->set_legacy_config("foo");
-
-  // Create a new trace session.
-  auto* tracing_session = NewTrace(cfg);
+  // Create a new trace session. Only the "foo" category is enabled. It also
+  // exists both locally and in the existing module.
+  auto* tracing_session = NewTraceWithCategories({"foo"});
   tracing_session->get()->StartBlocking();
 
   // Emit some track events locally and from the test module.
@@ -777,10 +928,7 @@
   }
 }
 
-TEST_F(PerfettoApiTest, TrackEventConcurrentSessions) {
-  // Check that categories that are enabled and disabled in two parallel tracing
-  // sessions don't interfere.
-
+TEST_F(PerfettoApiTest, TrackEventDynamicCategories) {
   // Setup the trace config.
   perfetto::TraceConfig cfg;
   cfg.set_duration_ms(500);
@@ -788,14 +936,72 @@
   auto* ds_cfg = cfg.add_data_sources()->mutable_config();
   ds_cfg->set_name("track_event");
 
+  // Session #1 enabled the "dynamic" category.
+  auto* tracing_session = NewTraceWithCategories({"dynamic"});
+  tracing_session->get()->StartBlocking();
+
+  // Session #2 enables "dynamic_2".
+  auto* tracing_session2 = NewTraceWithCategories({"dynamic_2"});
+  tracing_session2->get()->StartBlocking();
+
+  // Test naming dynamic categories with std::string.
+  perfetto::DynamicCategory dynamic{"dynamic"};
+  TRACE_EVENT_BEGIN(dynamic, "EventInDynamicCategory");
+  perfetto::DynamicCategory dynamic_disabled{"dynamic_disabled"};
+  TRACE_EVENT_BEGIN(dynamic_disabled, "EventInDisabledDynamicCategory");
+
+  // Test naming dynamic categories statically.
+  TRACE_EVENT_BEGIN("dynamic", "EventInStaticallyNamedDynamicCategory");
+
+  perfetto::DynamicCategory dynamic_2{"dynamic_2"};
+  TRACE_EVENT_BEGIN(dynamic_2, "EventInSecondDynamicCategory");
+  TRACE_EVENT_BEGIN("dynamic_2", "EventInSecondStaticallyNamedDynamicCategory");
+
+  std::thread thread([] {
+    // Make sure the category name can actually be computed at runtime.
+    std::string name = "dyn";
+    if (perfetto::base::GetThreadId())
+      name += "amic";
+    perfetto::DynamicCategory cat{name};
+    TRACE_EVENT_BEGIN(cat, "DynamicFromOtherThread");
+    perfetto::DynamicCategory cat2{"dynamic_disabled"};
+    TRACE_EVENT_BEGIN(cat2, "EventInDisabledDynamicCategory");
+  });
+  thread.join();
+
+  perfetto::TrackEvent::Flush();
+  tracing_session->get()->StopBlocking();
+  std::vector<char> raw_trace = tracing_session->get()->ReadTraceBlocking();
+  std::string trace(raw_trace.data(), raw_trace.size());
+  EXPECT_THAT(trace, HasSubstr("EventInDynamicCategory"));
+  EXPECT_THAT(trace, Not(HasSubstr("EventInDisabledDynamicCategory")));
+  EXPECT_THAT(trace, HasSubstr("DynamicFromOtherThread"));
+  EXPECT_THAT(trace, Not(HasSubstr("EventInSecondDynamicCategory")));
+  EXPECT_THAT(trace, HasSubstr("EventInStaticallyNamedDynamicCategory"));
+  EXPECT_THAT(trace,
+              Not(HasSubstr("EventInSecondStaticallyNamedDynamicCategory")));
+
+  tracing_session2->get()->StopBlocking();
+  raw_trace = tracing_session2->get()->ReadTraceBlocking();
+  trace = std::string(raw_trace.data(), raw_trace.size());
+  EXPECT_THAT(trace, Not(HasSubstr("EventInDynamicCategory")));
+  EXPECT_THAT(trace, Not(HasSubstr("EventInDisabledDynamicCategory")));
+  EXPECT_THAT(trace, Not(HasSubstr("DynamicFromOtherThread")));
+  EXPECT_THAT(trace, HasSubstr("EventInSecondDynamicCategory"));
+  EXPECT_THAT(trace, Not(HasSubstr("EventInStaticallyNamedDynamicCategory")));
+  EXPECT_THAT(trace, HasSubstr("EventInSecondStaticallyNamedDynamicCategory"));
+}
+
+TEST_F(PerfettoApiTest, TrackEventConcurrentSessions) {
+  // Check that categories that are enabled and disabled in two parallel tracing
+  // sessions don't interfere.
+
   // Session #1 enables the "foo" category.
-  ds_cfg->set_legacy_config("foo");
-  auto* tracing_session = NewTrace(cfg);
+  auto* tracing_session = NewTraceWithCategories({"foo"});
   tracing_session->get()->StartBlocking();
 
   // Session #2 enables the "bar" category.
-  ds_cfg->set_legacy_config("bar");
-  auto* tracing_session2 = NewTrace(cfg);
+  auto* tracing_session2 = NewTraceWithCategories({"bar"});
   tracing_session2->get()->StartBlocking();
 
   // Emit some track events under both categories.
@@ -935,16 +1141,8 @@
 }
 
 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);
+  auto* tracing_session = NewTraceWithCategories({"bar"});
   tracing_session->get()->StartBlocking();
 
   // Declare a custom track and give it a name.
@@ -1017,17 +1215,58 @@
   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");
-
+TEST_F(PerfettoApiTest, TrackEventCustomTrackAndTimestamp) {
   // Create a new trace session.
-  auto* tracing_session = NewTrace(cfg);
+  auto* tracing_session = NewTraceWithCategories({"bar"});
+  tracing_session->get()->StartBlocking();
+
+  // Custom track.
+  perfetto::Track track(789);
+
+  auto empty_lambda = [](perfetto::EventContext) {};
+  constexpr uint64_t kBeginEventTime = 10;
+  constexpr uint64_t kEndEventTime = 15;
+  TRACE_EVENT_BEGIN("bar", "Event", track, kBeginEventTime, empty_lambda);
+  TRACE_EVENT_END("bar", track, kEndEventTime, empty_lambda);
+
+  constexpr uint64_t kInstantEventTime = 1;
+  TRACE_EVENT_INSTANT("bar", "InstantEvent", track, kInstantEventTime,
+                      empty_lambda);
+
+  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()));
+
+  int event_count = 0;
+  for (const auto& packet : trace.packet()) {
+    if (!packet.has_track_event())
+      continue;
+    event_count++;
+    switch (packet.track_event().type()) {
+      case perfetto::protos::gen::TrackEvent::TYPE_SLICE_BEGIN:
+        EXPECT_EQ(packet.timestamp(), kBeginEventTime);
+        break;
+      case perfetto::protos::gen::TrackEvent::TYPE_SLICE_END:
+        EXPECT_EQ(packet.timestamp(), kEndEventTime);
+        break;
+      case perfetto::protos::gen::TrackEvent::TYPE_INSTANT:
+        EXPECT_EQ(packet.timestamp(), kInstantEventTime);
+        break;
+      case perfetto::protos::gen::TrackEvent::TYPE_COUNTER:
+      case perfetto::protos::gen::TrackEvent::TYPE_UNSPECIFIED:
+        ADD_FAILURE();
+    }
+  }
+  EXPECT_EQ(event_count, 3);
+  perfetto::TrackEvent::EraseTrackDescriptor(track);
+}
+
+TEST_F(PerfettoApiTest, TrackEventAnonymousCustomTrack) {
+  // Create a new trace session.
+  auto* tracing_session = NewTraceWithCategories({"bar"});
   tracing_session->get()->StartBlocking();
 
   // Emit an async event without giving it an explicit descriptor.
@@ -1060,16 +1299,8 @@
 }
 
 TEST_F(PerfettoApiTest, TrackEventTypedArgs) {
-  // 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("foo");
-
   // Create a new trace session.
-  auto* tracing_session = NewTrace(cfg);
+  auto* tracing_session = NewTraceWithCategories({"foo"});
   tracing_session->get()->StartBlocking();
 
   auto random_value = random();
@@ -1134,16 +1365,8 @@
 int InternedLogMessageBody::commit_count = 0;
 
 TEST_F(PerfettoApiTest, TrackEventTypedArgsWithInterning) {
-  // 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("foo");
-
   // Create a new trace session.
-  auto* tracing_session = NewTrace(cfg);
+  auto* tracing_session = NewTraceWithCategories({"foo"});
   tracing_session->get()->StartBlocking();
 
   std::stringstream large_message;
@@ -1209,16 +1432,8 @@
 };
 
 TEST_F(PerfettoApiTest, TrackEventTypedArgsWithInterningByValue) {
-  // 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("foo");
-
   // Create a new trace session.
-  auto* tracing_session = NewTrace(cfg);
+  auto* tracing_session = NewTraceWithCategories({"foo"});
   tracing_session->get()->StartBlocking();
 
   size_t body_iid;
@@ -1257,35 +1472,26 @@
 };
 
 TEST_F(PerfettoApiTest, TrackEventTypedArgsWithInterningByHashing) {
-  // 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("foo");
-
   // Create a new trace session.
-  auto* tracing_session = NewTrace(cfg);
+  auto* tracing_session = NewTraceWithCategories({"foo"});
   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();
@@ -1313,16 +1519,8 @@
 };
 
 TEST_F(PerfettoApiTest, TrackEventTypedArgsWithInterningComplexValue) {
-  // 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("foo");
-
   // Create a new trace session.
-  auto* tracing_session = NewTrace(cfg);
+  auto* tracing_session = NewTraceWithCategories({"foo"});
   tracing_session->get()->StartBlocking();
 
   TRACE_EVENT_BEGIN("foo", "EventWithState", [&](perfetto::EventContext ctx) {
@@ -1349,16 +1547,8 @@
 }
 
 TEST_F(PerfettoApiTest, TrackEventScoped) {
-  // 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("test");
-
   // Create a new trace session.
-  auto* tracing_session = NewTrace(cfg);
+  auto* tracing_session = NewTraceWithCategories({"test"});
   tracing_session->get()->StartBlocking();
 
   {
@@ -1389,16 +1579,8 @@
 }
 
 TEST_F(PerfettoApiTest, TrackEventInstant) {
-  // 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("test");
-
   // Create a new trace session.
-  auto* tracing_session = NewTrace(cfg);
+  auto* tracing_session = NewTraceWithCategories({"test"});
   tracing_session->get()->StartBlocking();
 
   TRACE_EVENT_INSTANT("test", "TestEvent");
@@ -1411,18 +1593,13 @@
 }
 
 TEST_F(PerfettoApiTest, TrackEventDebugAnnotations) {
-  // 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("test");
-
   // Create a new trace session.
-  auto* tracing_session = NewTrace(cfg);
+  auto* tracing_session = NewTraceWithCategories({"test"});
   tracing_session->get()->StartBlocking();
 
+  enum MyEnum { ENUM_FOO, ENUM_BAR };
+  enum MySignedEnum { SIGNED_ENUM_FOO = -1, SIGNED_ENUM_BAR };
+
   TRACE_EVENT_BEGIN("test", "E", "bool_arg", false);
   TRACE_EVENT_BEGIN("test", "E", "int_arg", -123);
   TRACE_EVENT_BEGIN("test", "E", "uint_arg", 456u);
@@ -1432,46 +1609,36 @@
                     std::string("tracing"));
   TRACE_EVENT_BEGIN("test", "E", "ptr_arg",
                     reinterpret_cast<void*>(0xbaadf00d));
+  TRACE_EVENT_BEGIN("test", "E", "size_t_arg", size_t{42});
+  TRACE_EVENT_BEGIN("test", "E", "ptrdiff_t_arg", ptrdiff_t{-7});
+  TRACE_EVENT_BEGIN("test", "E", "enum_arg", ENUM_BAR);
+  TRACE_EVENT_BEGIN("test", "E", "signed_enum_arg", SIGNED_ENUM_FOO);
   perfetto::TrackEvent::Flush();
 
   tracing_session->get()->StopBlocking();
   auto slices = ReadSlicesFromTrace(tracing_session->get());
   EXPECT_THAT(
       slices,
-      ElementsAre("B:test.E(bool_arg=(bool)0)", "B:test.E(int_arg=(int)-123)",
-                  "B:test.E(uint_arg=(uint)456)",
-                  "B:test.E(float_arg=(double)3.14159)",
-                  "B:test.E(double_arg=(double)6.22)",
-                  "B:test.E(str_arg=(string)hello,str_arg2=(string)tracing)",
-                  "B:test.E(ptr_arg=(pointer)baadf00d)"));
+      ElementsAre(
+          "B:test.E(bool_arg=(bool)0)", "B:test.E(int_arg=(int)-123)",
+          "B:test.E(uint_arg=(uint)456)", "B:test.E(float_arg=(double)3.14159)",
+          "B:test.E(double_arg=(double)6.22)",
+          "B:test.E(str_arg=(string)hello,str_arg2=(string)tracing)",
+          "B:test.E(ptr_arg=(pointer)baadf00d)",
+          "B:test.E(size_t_arg=(uint)42)", "B:test.E(ptrdiff_t_arg=(int)-7)",
+          "B:test.E(enum_arg=(uint)1)", "B:test.E(signed_enum_arg=(int)-1)"));
 }
 
 TEST_F(PerfettoApiTest, TrackEventCustomDebugAnnotations) {
-  // 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("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);
+  auto* tracing_session = NewTraceWithCategories({"test"});
   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();
@@ -1484,14 +1651,6 @@
 }
 
 TEST_F(PerfettoApiTest, TrackEventCustomRawDebugAnnotations) {
-  // 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("test");
-
   // Note: this class is also testing a non-moveable and non-copiable argument.
   class MyRawDebugAnnotation : public perfetto::DebugAnnotation {
    public:
@@ -1517,7 +1676,7 @@
   };
 
   // Create a new trace session.
-  auto* tracing_session = NewTrace(cfg);
+  auto* tracing_session = NewTraceWithCategories({"test"});
   tracing_session->get()->StartBlocking();
 
   TRACE_EVENT_BEGIN("test", "E", "raw_arg", MyRawDebugAnnotation());
@@ -1545,26 +1704,25 @@
   auto* tracing_session = NewTrace(cfg);
   tracing_session->get()->StartBlocking();
 
+  // New macros require perfetto::StaticString{} annotation.
   for (int i = 0; i < 3; i++)
-    TRACE_EVENT_BEGIN("test", i % 2 ? "Odd" : "Even");
+    TRACE_EVENT_BEGIN("test", perfetto::StaticString{i % 2 ? "Odd" : "Even"});
+
+  // Legacy macros assume all arguments are static strings.
+  for (int i = 0; i < 3; i++)
+    TRACE_EVENT_BEGIN0("test", i % 2 ? "Odd" : "Even");
+
   perfetto::TrackEvent::Flush();
 
   tracing_session->get()->StopBlocking();
   auto slices = ReadSlicesFromTrace(tracing_session->get());
-  EXPECT_THAT(slices, ElementsAre("B:test.Even", "B:test.Odd", "B:test.Even"));
+  EXPECT_THAT(slices, ElementsAre("B:test.Even", "B:test.Odd", "B:test.Even",
+                                  "B:test.Even", "B:test.Odd", "B:test.Even"));
 }
 
 TEST_F(PerfettoApiTest, TrackEventArgumentsNotEvaluatedWhenDisabled) {
-  // 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("foo");
-
   // Create a new trace session.
-  auto* tracing_session = NewTrace(cfg);
+  auto* tracing_session = NewTraceWithCategories({"foo"});
   tracing_session->get()->StartBlocking();
 
   bool called = false;
@@ -1584,6 +1742,140 @@
   EXPECT_TRUE(called);
 }
 
+TEST_F(PerfettoApiTest, TrackEventConfig) {
+  auto check_config = [&](perfetto::protos::gen::TrackEventConfig te_cfg) {
+    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_track_event_config_raw(te_cfg.SerializeAsString());
+
+    auto* tracing_session = NewTrace(cfg);
+    tracing_session->get()->StartBlocking();
+
+    TRACE_EVENT_BEGIN("foo", "FooEvent");
+    TRACE_EVENT_BEGIN("bar", "BarEvent");
+    TRACE_EVENT_BEGIN("foo,bar", "MultiFooBar");
+    TRACE_EVENT_BEGIN("baz,bar,quux", "MultiBar");
+    TRACE_EVENT_BEGIN("red,green,blue,foo", "MultiFoo");
+    TRACE_EVENT_BEGIN("red,green,blue,yellow", "MultiNone");
+    TRACE_EVENT_BEGIN("cat", "SlowEvent");
+    TRACE_EVENT_BEGIN("cat.verbose", "DebugEvent");
+    TRACE_EVENT_BEGIN("test", "TagEvent");
+    TRACE_EVENT_BEGIN(TRACE_DISABLED_BY_DEFAULT("cat"), "SlowDisabledEvent");
+    TRACE_EVENT_BEGIN("dynamic,foo", "DynamicGroupFooEvent");
+    perfetto::DynamicCategory dyn{"dynamic,bar"};
+    TRACE_EVENT_BEGIN(dyn, "DynamicGroupBarEvent");
+
+    perfetto::TrackEvent::Flush();
+    tracing_session->get()->StopBlocking();
+    auto slices = ReadSlicesFromTrace(tracing_session->get());
+    tracing_session->session.reset();
+    return slices;
+  };
+
+  // Empty config should enable all categories except slow ones.
+  {
+    perfetto::protos::gen::TrackEventConfig te_cfg;
+    auto slices = check_config(te_cfg);
+    EXPECT_THAT(
+        slices,
+        ElementsAre("B:foo.FooEvent", "B:bar.BarEvent", "B:foo,bar.MultiFooBar",
+                    "B:baz,bar,quux.MultiBar", "B:red,green,blue,foo.MultiFoo",
+                    "B:test.TagEvent", "B:$dynamic,$foo.DynamicGroupFooEvent",
+                    "B:$dynamic,$bar.DynamicGroupBarEvent"));
+  }
+
+  // Enable exactly one category.
+  {
+    perfetto::protos::gen::TrackEventConfig te_cfg;
+    te_cfg.add_disabled_categories("*");
+    te_cfg.add_enabled_categories("foo");
+    auto slices = check_config(te_cfg);
+    EXPECT_THAT(slices, ElementsAre("B:foo.FooEvent", "B:foo,bar.MultiFooBar",
+                                    "B:red,green,blue,foo.MultiFoo",
+                                    "B:$dynamic,$foo.DynamicGroupFooEvent"));
+  }
+
+  // Enable two categories.
+  {
+    perfetto::protos::gen::TrackEventConfig te_cfg;
+    te_cfg.add_disabled_categories("*");
+    te_cfg.add_enabled_categories("foo");
+    te_cfg.add_enabled_categories("baz");
+    te_cfg.add_enabled_categories("bar");
+    auto slices = check_config(te_cfg);
+    EXPECT_THAT(
+        slices,
+        ElementsAre("B:foo.FooEvent", "B:bar.BarEvent", "B:foo,bar.MultiFooBar",
+                    "B:baz,bar,quux.MultiBar", "B:red,green,blue,foo.MultiFoo",
+                    "B:$dynamic,$foo.DynamicGroupFooEvent",
+                    "B:$dynamic,$bar.DynamicGroupBarEvent"));
+  }
+
+  // Enabling all categories with a pattern doesn't enable slow ones.
+  {
+    perfetto::protos::gen::TrackEventConfig te_cfg;
+    te_cfg.add_enabled_categories("*");
+    auto slices = check_config(te_cfg);
+    EXPECT_THAT(
+        slices,
+        ElementsAre("B:foo.FooEvent", "B:bar.BarEvent", "B:foo,bar.MultiFooBar",
+                    "B:baz,bar,quux.MultiBar", "B:red,green,blue,foo.MultiFoo",
+                    "B:test.TagEvent", "B:$dynamic,$foo.DynamicGroupFooEvent",
+                    "B:$dynamic,$bar.DynamicGroupBarEvent"));
+  }
+
+  // Enable with a pattern.
+  {
+    perfetto::protos::gen::TrackEventConfig te_cfg;
+    te_cfg.add_disabled_categories("*");
+    te_cfg.add_enabled_categories("fo*");
+    auto slices = check_config(te_cfg);
+    EXPECT_THAT(slices, ElementsAre("B:foo.FooEvent", "B:foo,bar.MultiFooBar",
+                                    "B:red,green,blue,foo.MultiFoo",
+                                    "B:$dynamic,$foo.DynamicGroupFooEvent"));
+  }
+
+  // Enable with a tag.
+  {
+    perfetto::protos::gen::TrackEventConfig te_cfg;
+    te_cfg.add_disabled_categories("*");
+    te_cfg.add_enabled_tags("tag");
+    auto slices = check_config(te_cfg);
+    EXPECT_THAT(slices, ElementsAre("B:test.TagEvent"));
+  }
+
+  // Enable just slow categories.
+  {
+    perfetto::protos::gen::TrackEventConfig te_cfg;
+    te_cfg.add_disabled_categories("*");
+    te_cfg.add_enabled_tags("slow");
+    auto slices = check_config(te_cfg);
+    EXPECT_THAT(slices,
+                ElementsAre("B:cat.SlowEvent",
+                            "B:disabled-by-default-cat.SlowDisabledEvent"));
+  }
+
+  // Enable everything including slow/debug categories.
+  {
+    perfetto::protos::gen::TrackEventConfig te_cfg;
+    te_cfg.add_enabled_categories("*");
+    te_cfg.add_enabled_tags("slow");
+    te_cfg.add_enabled_tags("debug");
+    auto slices = check_config(te_cfg);
+    EXPECT_THAT(slices,
+                ElementsAre("B:foo.FooEvent", "B:bar.BarEvent",
+                            "B:foo,bar.MultiFooBar", "B:baz,bar,quux.MultiBar",
+                            "B:red,green,blue,foo.MultiFoo", "B:cat.SlowEvent",
+                            "B:cat.verbose.DebugEvent", "B:test.TagEvent",
+                            "B:disabled-by-default-cat.SlowDisabledEvent",
+                            "B:$dynamic,$foo.DynamicGroupFooEvent",
+                            "B:$dynamic,$bar.DynamicGroupBarEvent"));
+  }
+}
+
 TEST_F(PerfettoApiTest, OneDataSourceOneEvent) {
   auto* data_source = &data_sources_["my_data_source"];
 
@@ -1949,10 +2241,13 @@
 }
 
 TEST_F(PerfettoApiTest, LegacyTraceEvents) {
-  // TODO(skyostil): For now we just test that all variants of legacy trace
-  // points compile. Test actual functionality when implemented.
+  // Create a new trace session.
+  auto* tracing_session =
+      NewTraceWithCategories({"cat", TRACE_DISABLED_BY_DEFAULT("cat")});
+  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);
 
@@ -1965,17 +2260,207 @@
 
   // Event with timestamp.
   TRACE_EVENT_INSTANT_WITH_TIMESTAMP0("cat", "LegacyInstantEvent",
-                                      TRACE_EVENT_SCOPE_GLOBAL, 123456789ul);
+                                      TRACE_EVENT_SCOPE_GLOBAL,
+                                      MyTimestamp{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);
+      "cat", std::string("LegacyWithIdTidAndTimestamp").c_str(), 1,
+      MyThreadId(123, 456), MyTimestamp{3});
 
   // Event with id.
-  TRACE_COUNTER_ID1("cat", "LegacyCounter", 1234, 9000);
+  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",
+          "B(bind_id=3671771902)(flow_direction=1):disabled-by-default-cat."
+          "LegacyFlowEvent",
+          "I:cat.LegacyInstantEvent",
+          "Legacy_S(unscoped_id=1)(pid_override=123)(tid_override=456):cat."
+          "LegacyWithIdTidAndTimestamp",
+          "Legacy_C:cat.LegacyCounter(value=(int)1234)",
+          "Legacy_C(unscoped_id=1234):cat.LegacyCounterWithId(value=(int)9000)",
+          "Legacy_M:cat.LegacyMetadata"));
+}
+
+TEST_F(PerfettoApiTest, LegacyTraceEventsWithCustomAnnotation) {
+  // Create a new trace session.
+  auto* tracing_session = NewTraceWithCategories({"cat"});
+  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.
+
+  auto* tracing_session = NewTraceWithCategories({"cat"});
+  tracing_session->get()->StartBlocking();
+
+  auto* tracing_session2 = NewTraceWithCategories({"cat"});
+  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})"));
+}
+
+TEST_F(PerfettoApiTest, LegacyTraceEventsWithId) {
+  auto* tracing_session = NewTraceWithCategories({"cat"});
+  tracing_session->get()->StartBlocking();
+
+  TRACE_EVENT_ASYNC_BEGIN0("cat", "UnscopedId", 0x1000);
+  TRACE_EVENT_ASYNC_BEGIN0("cat", "LocalId", TRACE_ID_LOCAL(0x2000));
+  TRACE_EVENT_ASYNC_BEGIN0("cat", "GlobalId", TRACE_ID_GLOBAL(0x3000));
+  TRACE_EVENT_ASYNC_BEGIN0(
+      "cat", "WithScope",
+      TRACE_ID_WITH_SCOPE("scope string", TRACE_ID_GLOBAL(0x4000)));
+
+  perfetto::TrackEvent::Flush();
+  tracing_session->get()->StopBlocking();
+  auto slices = ReadSlicesFromTrace(tracing_session->get());
+  EXPECT_THAT(slices, ElementsAre("Legacy_S(unscoped_id=4096):cat.UnscopedId",
+                                  "Legacy_S(local_id=8192):cat.LocalId",
+                                  "Legacy_S(global_id=12288):cat.GlobalId",
+                                  "Legacy_S(global_id=16384)(id_scope=\"scope "
+                                  "string\"):cat.WithScope"));
+}
+
+TEST_F(PerfettoApiTest, LegacyTraceEventsWithFlow) {
+  auto* tracing_session = NewTraceWithCategories({"cat"});
+  tracing_session->get()->StartBlocking();
+
+  const uint64_t flow_id = 1234;
+  {
+    TRACE_EVENT_WITH_FLOW1("cat", "LatencyInfo.Flow", TRACE_ID_GLOBAL(flow_id),
+                           TRACE_EVENT_FLAG_FLOW_OUT, "step", "Begin");
+  }
+
+  {
+    TRACE_EVENT_WITH_FLOW2("cat", "LatencyInfo.Flow", TRACE_ID_GLOBAL(flow_id),
+                           TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
+                           "step", "Middle", "value", false);
+  }
+
+  {
+    TRACE_EVENT_WITH_FLOW1("cat", "LatencyInfo.Flow", TRACE_ID_GLOBAL(flow_id),
+                           TRACE_EVENT_FLAG_FLOW_IN, "step", "End");
+  }
+
+  perfetto::TrackEvent::Flush();
+  tracing_session->get()->StopBlocking();
+  auto slices = ReadSlicesFromTrace(tracing_session->get());
+  EXPECT_THAT(slices,
+              ElementsAre("B(bind_id=1234)(flow_direction=2):cat.LatencyInfo."
+                          "Flow(step=(string)Begin)",
+                          "E",
+                          "B(bind_id=1234)(flow_direction=3):cat.LatencyInfo."
+                          "Flow(step=(string)Middle,value=(bool)0)",
+                          "E",
+                          "B(bind_id=1234)(flow_direction=1):cat.LatencyInfo."
+                          "Flow(step=(string)End)",
+                          "E"));
+}
+
+TEST_F(PerfettoApiTest, LegacyCategoryGroupEnabledState) {
+  bool foo_status;
+  bool bar_status;
+  bool dynamic_status;
+  TRACE_EVENT_CATEGORY_GROUP_ENABLED("foo", &foo_status);
+  TRACE_EVENT_CATEGORY_GROUP_ENABLED("bar", &bar_status);
+  TRACE_EVENT_CATEGORY_GROUP_ENABLED("dynamic", &dynamic_status);
+  EXPECT_FALSE(foo_status);
+  EXPECT_FALSE(bar_status);
+  EXPECT_FALSE(dynamic_status);
+
+  const uint8_t* foo_enabled =
+      TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED("foo");
+  const uint8_t* bar_enabled =
+      TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED("bar");
+  EXPECT_FALSE(*foo_enabled);
+  EXPECT_FALSE(*bar_enabled);
+
+  auto* tracing_session = NewTraceWithCategories({"foo", "dynamic"});
+  tracing_session->get()->StartBlocking();
+  TRACE_EVENT_CATEGORY_GROUP_ENABLED("foo", &foo_status);
+  TRACE_EVENT_CATEGORY_GROUP_ENABLED("bar", &bar_status);
+  TRACE_EVENT_CATEGORY_GROUP_ENABLED("dynamic", &dynamic_status);
+  EXPECT_TRUE(foo_status);
+  EXPECT_FALSE(bar_status);
+  EXPECT_TRUE(dynamic_status);
+
+  EXPECT_TRUE(*foo_enabled);
+  EXPECT_FALSE(*bar_enabled);
+
+  tracing_session->get()->StopBlocking();
+  TRACE_EVENT_CATEGORY_GROUP_ENABLED("foo", &foo_status);
+  TRACE_EVENT_CATEGORY_GROUP_ENABLED("bar", &bar_status);
+  TRACE_EVENT_CATEGORY_GROUP_ENABLED("dynamic", &dynamic_status);
+  EXPECT_FALSE(foo_status);
+  EXPECT_FALSE(bar_status);
+  EXPECT_FALSE(dynamic_status);
+  EXPECT_FALSE(*foo_enabled);
+  EXPECT_FALSE(*bar_enabled);
+}
+
+TEST_F(PerfettoApiTest, CategoryEnabledState) {
+  perfetto::DynamicCategory dynamic{"dynamic"};
+  EXPECT_FALSE(TRACE_EVENT_CATEGORY_ENABLED("foo"));
+  EXPECT_FALSE(TRACE_EVENT_CATEGORY_ENABLED("bar"));
+  EXPECT_FALSE(TRACE_EVENT_CATEGORY_ENABLED("red,green,blue,foo"));
+  EXPECT_FALSE(TRACE_EVENT_CATEGORY_ENABLED("dynamic"));
+  EXPECT_FALSE(TRACE_EVENT_CATEGORY_ENABLED("dynamic_2"));
+  EXPECT_FALSE(TRACE_EVENT_CATEGORY_ENABLED(dynamic));
+
+  auto* tracing_session = NewTraceWithCategories({"foo", "dynamic"});
+  tracing_session->get()->StartBlocking();
+  EXPECT_TRUE(TRACE_EVENT_CATEGORY_ENABLED("foo"));
+  EXPECT_FALSE(TRACE_EVENT_CATEGORY_ENABLED("bar"));
+  EXPECT_TRUE(TRACE_EVENT_CATEGORY_ENABLED("red,green,blue,foo"));
+  EXPECT_TRUE(TRACE_EVENT_CATEGORY_ENABLED("dynamic"));
+  EXPECT_FALSE(TRACE_EVENT_CATEGORY_ENABLED("dynamic_2"));
+  EXPECT_TRUE(TRACE_EVENT_CATEGORY_ENABLED(dynamic));
+
+  tracing_session->get()->StopBlocking();
+  EXPECT_FALSE(TRACE_EVENT_CATEGORY_ENABLED("foo"));
+  EXPECT_FALSE(TRACE_EVENT_CATEGORY_ENABLED("bar"));
+  EXPECT_FALSE(TRACE_EVENT_CATEGORY_ENABLED("red,green,blue,foo"));
+  EXPECT_FALSE(TRACE_EVENT_CATEGORY_ENABLED("dynamic"));
+  EXPECT_FALSE(TRACE_EVENT_CATEGORY_ENABLED("dynamic_2"));
+  EXPECT_FALSE(TRACE_EVENT_CATEGORY_ENABLED(dynamic));
 }
 
 }  // namespace
diff --git a/src/tracing/test/api_test_support.cc b/src/tracing/test/api_test_support.cc
index 1667ffc..5689a74 100644
--- a/src/tracing/test/api_test_support.cc
+++ b/src/tracing/test/api_test_support.cc
@@ -26,14 +26,5 @@
   return static_cast<int32_t>(base::GetProcessId());
 }
 
-uint64_t GetTraceTimeNs() {
-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX) && \
-    !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  return static_cast<uint64_t>(perfetto::base::GetBootTimeNs().count());
-#else
-  return static_cast<uint64_t>(perfetto::base::GetWallTimeNs().count());
-#endif
-}
-
 }  // namespace test
 }  // namespace perfetto
diff --git a/src/tracing/test/api_test_support.h b/src/tracing/test/api_test_support.h
index 031afdf..9409caf 100644
--- a/src/tracing/test/api_test_support.h
+++ b/src/tracing/test/api_test_support.h
@@ -32,7 +32,6 @@
 namespace test {
 
 int32_t GetCurrentProcessId();
-uint64_t GetTraceTimeNs();
 
 }  // namespace test
 }  // namespace perfetto
diff --git a/src/tracing/test/fake_producer_endpoint.h b/src/tracing/test/fake_producer_endpoint.h
index fc364cf..a3148d9 100644
--- a/src/tracing/test/fake_producer_endpoint.h
+++ b/src/tracing/test/fake_producer_endpoint.h
@@ -37,6 +37,7 @@
   void NotifyDataSourceStarted(DataSourceInstanceID) override {}
   void NotifyDataSourceStopped(DataSourceInstanceID) override {}
   void ActivateTriggers(const std::vector<std::string>&) override {}
+  void Sync(std::function<void()>) override {}
   SharedMemory* shared_memory() const override { return nullptr; }
   size_t shared_buffer_page_size_kb() const override { return 0; }
   std::unique_ptr<TraceWriter> CreateTraceWriter(
@@ -44,7 +45,8 @@
       BufferExhaustedPolicy) override {
     return nullptr;
   }
-  SharedMemoryArbiter* GetInProcessShmemArbiter() override { return nullptr; }
+  SharedMemoryArbiter* MaybeSharedMemoryArbiter() override { return nullptr; }
+  bool IsShmemProvidedByProducer() const override { return false; }
 
   CommitDataRequest last_commit_data_request;
   CommitDataCallback last_commit_data_callback;
diff --git a/src/tracing/test/mock_producer.cc b/src/tracing/test/mock_producer.cc
index 39ee0a8..0679847 100644
--- a/src/tracing/test/mock_producer.cc
+++ b/src/tracing/test/mock_producer.cc
@@ -48,12 +48,13 @@
                            const std::string& producer_name,
                            uid_t uid,
                            size_t shared_memory_size_hint_bytes,
-                           size_t shared_memory_page_size_hint_bytes) {
+                           size_t shared_memory_page_size_hint_bytes,
+                           std::unique_ptr<SharedMemory> shm) {
   producer_name_ = producer_name;
   service_endpoint_ = svc->ConnectProducer(
       this, uid, producer_name, shared_memory_size_hint_bytes,
       /*in_process=*/true, TracingService::ProducerSMBScrapingMode::kDefault,
-      shared_memory_page_size_hint_bytes);
+      shared_memory_page_size_hint_bytes, std::move(shm));
   auto checkpoint_name = "on_producer_connect_" + producer_name;
   auto on_connect = task_runner_->CreateCheckpoint(checkpoint_name);
   EXPECT_CALL(*this, OnConnect()).WillOnce(Invoke(on_connect));
diff --git a/src/tracing/test/mock_producer.h b/src/tracing/test/mock_producer.h
index 7b23f2c..0cbd4e2 100644
--- a/src/tracing/test/mock_producer.h
+++ b/src/tracing/test/mock_producer.h
@@ -22,6 +22,7 @@
 #include <string>
 
 #include "perfetto/ext/tracing/core/producer.h"
+#include "perfetto/ext/tracing/core/shared_memory.h"
 #include "perfetto/ext/tracing/core/trace_writer.h"
 #include "perfetto/ext/tracing/core/tracing_service.h"
 #include "test/gtest_and_gmock.h"
@@ -47,7 +48,8 @@
                const std::string& producer_name,
                uid_t uid = 42,
                size_t shared_memory_size_hint_bytes = 0,
-               size_t shared_memory_page_size_hint_bytes = 0);
+               size_t shared_memory_page_size_hint_bytes = 0,
+               std::unique_ptr<SharedMemory> shm = nullptr);
   void RegisterDataSource(const std::string& name,
                           bool ack_stop = false,
                           bool ack_start = false,
diff --git a/src/tracing/test/test_shared_memory.h b/src/tracing/test/test_shared_memory.h
index 9db6bcb..2a323bf 100644
--- a/src/tracing/test/test_shared_memory.h
+++ b/src/tracing/test/test_shared_memory.h
@@ -41,6 +41,7 @@
 
   void* start() const override { return mem_.Get(); }
   size_t size() const override { return size_; }
+  int fd() const override { return -1; }
 
   base::PagedMemory mem_;
   size_t size_;
diff --git a/src/tracing/test/tracing_module.cc b/src/tracing/test/tracing_module.cc
index 7c10d4e..d4fef2e 100644
--- a/src/tracing/test/tracing_module.cc
+++ b/src/tracing/test/tracing_module.cc
@@ -89,4 +89,18 @@
   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 c005a6f..7ed9364 100644
--- a/src/tracing/test/tracing_module.h
+++ b/src/tracing/test/tracing_module.h
@@ -39,6 +39,10 @@
 void FunctionWithOneTrackEventWithDebugAnnotations();
 void FunctionWithOneTrackEventWithCustomTrack();
 
+// Legacy events.
+void FunctionWithOneLegacyEvent();
+void FunctionWithOneScopedLegacyEvent();
+
 }  // namespace tracing_module
 
 #endif  // SRC_TRACING_TEST_TRACING_MODULE_H_
diff --git a/src/tracing/test/tracing_module_categories.h b/src/tracing/test/tracing_module_categories.h
index f776894..03c0425 100644
--- a/src/tracing/test/tracing_module_categories.h
+++ b/src/tracing/test/tracing_module_categories.h
@@ -23,9 +23,11 @@
 // 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"
 
+// Note: Using the old syntax here to ensure backwards compatibility.
 PERFETTO_DEFINE_CATEGORIES(PERFETTO_CATEGORY(cat1),
                            PERFETTO_CATEGORY(cat2),
                            PERFETTO_CATEGORY(cat3),
diff --git a/src/tracing/tracing.cc b/src/tracing/tracing.cc
index 8b20b7f..c0149d9 100644
--- a/src/tracing/tracing.cc
+++ b/src/tracing/tracing.cc
@@ -24,7 +24,7 @@
 namespace perfetto {
 
 // static
-void Tracing::Initialize(const TracingInitArgs& args) {
+void Tracing::InitializeInternal(const TracingInitArgs& args) {
   static bool was_initialized = false;
   static TracingInitArgs init_args;
   if (was_initialized) {
diff --git a/src/tracing/track_event_category_registry.cc b/src/tracing/track_event_category_registry.cc
index e64fa3b..6d9a649 100644
--- a/src/tracing/track_event_category_registry.cc
+++ b/src/tracing/track_event_category_registry.cc
@@ -17,10 +17,31 @@
 #include "perfetto/tracing/track_event_category_registry.h"
 
 namespace perfetto {
+
+// static
+Category Category::FromDynamicCategory(const char* name) {
+  if (GetNthNameSize(1, name, name)) {
+    Category group(Group(name));
+    PERFETTO_DCHECK(group.name);
+    return group;
+  }
+  Category category(name);
+  PERFETTO_DCHECK(category.name);
+  return category;
+}
+
+Category Category::FromDynamicCategory(
+    const DynamicCategory& dynamic_category) {
+  return FromDynamicCategory(dynamic_category.name.c_str());
+}
+
 namespace internal {
 
-const TrackEventCategory* TrackEventCategoryRegistry::GetCategory(
-    size_t index) const {
+perfetto::DynamicCategory NullCategory(const perfetto::DynamicCategory&) {
+  return perfetto::DynamicCategory{};
+}
+
+const Category* TrackEventCategoryRegistry::GetCategory(size_t index) const {
   PERFETTO_DCHECK(index < category_count_);
   return &categories_[index];
 }
diff --git a/src/tracing/track_event_legacy.cc b/src/tracing/track_event_legacy.cc
new file mode 100644
index 0000000..9a687c6
--- /dev/null
+++ b/src/tracing/track_event_legacy.cc
@@ -0,0 +1,71 @@
+/*
+ * 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 "perfetto/tracing/track_event_legacy.h"
+
+#include "perfetto/tracing/track.h"
+
+namespace perfetto {
+namespace legacy {
+
+template <>
+bool ConvertThreadId(const PerfettoLegacyCurrentThreadId&,
+                     uint64_t*,
+                     int32_t*,
+                     int32_t*) {
+  // No need to override anything for events on to the current thread.
+  return false;
+}
+
+}  // namespace legacy
+
+namespace internal {
+
+void LegacyTraceId::Write(protos::pbzero::TrackEvent::LegacyEvent* event,
+                          uint32_t event_flags) const {
+  // Legacy flow events always use bind_id.
+  if (event_flags &
+      (legacy::kTraceEventFlagFlowOut | legacy::kTraceEventFlagFlowIn)) {
+    // Flow bind_ids don't have scopes, so we need to mangle in-process ones to
+    // avoid collisions.
+    if (id_flags_ & legacy::kTraceEventFlagHasLocalId) {
+      event->set_bind_id(raw_id_ ^ ProcessTrack::Current().uuid);
+    } else {
+      event->set_bind_id(raw_id_);
+    }
+    return;
+  }
+
+  uint32_t scope_flags = id_flags_ & (legacy::kTraceEventFlagHasId |
+                                      legacy::kTraceEventFlagHasLocalId |
+                                      legacy::kTraceEventFlagHasGlobalId);
+  switch (scope_flags) {
+    case legacy::kTraceEventFlagHasId:
+      event->set_unscoped_id(raw_id_);
+      break;
+    case legacy::kTraceEventFlagHasLocalId:
+      event->set_local_id(raw_id_);
+      break;
+    case legacy::kTraceEventFlagHasGlobalId:
+      event->set_global_id(raw_id_);
+      break;
+  }
+  if (scope_)
+    event->set_id_scope(scope_);
+}
+
+}  // namespace internal
+}  // namespace perfetto
diff --git a/test/BUILD.gn b/test/BUILD.gn
index 0fe00be..c7b7c15 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -22,6 +22,7 @@
     ":test_helper",
     "../gn:default_deps",
     "../gn:gtest_and_gmock",
+    "../include/perfetto/ext/ipc",
     "../include/perfetto/ext/traced",
     "../include/perfetto/protozero",
     "../protos/perfetto/config:cpp",
@@ -35,9 +36,15 @@
     "../src/base:test_support",
     "../src/traced/probes/ftrace",
   ]
-  sources = [
-    "end_to_end_integrationtest.cc",
+
+  # These binaries are requires by the cmdline tests, which invoke perfetto
+  # and trigger_perfetto via Subprocess.
+  data_deps = [
+    "../src/perfetto_cmd:perfetto",
+    "../src/perfetto_cmd:trigger_perfetto",
   ]
+
+  sources = [ "end_to_end_integrationtest.cc" ]
   if (start_daemons_for_testing) {
     cflags = [ "-DPERFETTO_START_DAEMONS_FOR_TESTING" ]
 
@@ -52,9 +59,7 @@
 }
 
 executable("client_api_example") {
-  sources = [
-    "client_api_example.cc",
-  ]
+  sources = [ "client_api_example.cc" ]
   deps = [
     "..:libperfetto_client_experimental",
     "../gn:default_deps",
@@ -66,9 +71,7 @@
 }
 
 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 = [
     ":test_helper",
@@ -76,15 +79,14 @@
     "../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,7 +100,9 @@
   testonly = true
   public_deps = [
     "../protos/perfetto/trace:cpp",
-    "../src/tracing:ipc",
+    "../src/tracing/ipc/consumer",
+    "../src/tracing/ipc/producer",
+    "../src/tracing/ipc/service",
   ]
   deps = [
     "../gn:default_deps",
@@ -107,7 +111,7 @@
     "../protos/perfetto/trace:zero",
     "../src/base:test_support",
     "../src/traced/probes:probes_src",
-    "../src/tracing:ipc",
+    "../src/tracing/ipc:common",
   ]
   sources = [
     "fake_producer.cc",
@@ -134,9 +138,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" ]
     }
@@ -148,8 +150,20 @@
       "../gn:benchmark",
       "../gn:default_deps",
     ]
-    sources = [
-      "benchmark_main.cc",
-    ]
+    sources = [ "benchmark_main.cc" ]
   }
 }  # if (enable_perfetto_benchmarks)
+
+if (perfetto_build_with_android || (is_android && perfetto_build_standalone)) {
+  # This is used only in-tree builds. It's built in standalone builds just to
+  # get build coverage.
+  static_library("perfetto_gtest_logcat_printer") {
+    testonly = true
+    complete_static_lib = true
+    sources = [ "gtest_logcat_printer.cc" ]
+    deps = [
+      "../gn:default_deps",
+      "../gn:gtest_and_gmock",
+    ]
+  }
+}
diff --git a/test/ci/android_tests.sh b/test/ci/android_tests.sh
index 89d27cf..da3d645 100755
--- a/test/ci/android_tests.sh
+++ b/test/ci/android_tests.sh
@@ -13,7 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-INSTALL_BUILD_DEPS_ARGS=""  # Run without args, without --no-android.
+INSTALL_BUILD_DEPS_ARGS="--android"
 source $(dirname ${BASH_SOURCE[0]})/common.sh
 
 # Run the emulator earlier so by the time we build it's booted.
diff --git a/test/ci/bazel_tests.sh b/test/ci/bazel_tests.sh
index 6897066..4af0b95 100755
--- a/test/ci/bazel_tests.sh
+++ b/test/ci/bazel_tests.sh
@@ -13,7 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-INSTALL_BUILD_DEPS_ARGS="--no-android"
+INSTALL_BUILD_DEPS_ARGS=""
 source $(dirname ${BASH_SOURCE[0]})/common.sh
 
 bazel build //:all --verbose_failures
diff --git a/test/ci/fuzzer_tests.sh b/test/ci/fuzzer_tests.sh
index 9ef9738..9eaa684 100755
--- a/test/ci/fuzzer_tests.sh
+++ b/test/ci/fuzzer_tests.sh
@@ -13,7 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-INSTALL_BUILD_DEPS_ARGS="--no-android"
+INSTALL_BUILD_DEPS_ARGS=""
 source $(dirname ${BASH_SOURCE[0]})/common.sh
 
 tools/gn gen ${OUT_PATH} --args="${PERFETTO_TEST_GN_ARGS}" --check
diff --git a/test/ci/linux_tests.sh b/test/ci/linux_tests.sh
index 62bf1c7..3db44cc 100755
--- a/test/ci/linux_tests.sh
+++ b/test/ci/linux_tests.sh
@@ -13,7 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-INSTALL_BUILD_DEPS_ARGS="--no-android"
+INSTALL_BUILD_DEPS_ARGS=""
 source $(dirname ${BASH_SOURCE[0]})/common.sh
 
 tools/gn gen ${OUT_PATH} --args="${PERFETTO_TEST_GN_ARGS}" --check
@@ -38,13 +38,8 @@
 mkdir -p /ci/artifacts/perf
 
 tools/diff_test_trace_processor.py \
-  --test-type=queries \
-  --perf-file=/ci/artifacts/perf/tp-perf-queries.json \
-  ${TP_SHELL}
-
-tools/diff_test_trace_processor.py \
-  --test-type=metrics \
-  --perf-file=/ci/artifacts/perf/tp-perf-metrics.json \
+  --test-type=all \
+  --perf-file=/ci/artifacts/perf/tp-perf-all.json \
   ${TP_SHELL}
 
 # Don't run benchmarks under sanitizers or debug, too slow and pointless.
diff --git a/test/ci/ui_tests.sh b/test/ci/ui_tests.sh
index 5476840..6b59820 100755
--- a/test/ci/ui_tests.sh
+++ b/test/ci/ui_tests.sh
@@ -13,7 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-INSTALL_BUILD_DEPS_ARGS="--no-android --ui"
+INSTALL_BUILD_DEPS_ARGS="--ui"
 source $(dirname ${BASH_SOURCE[0]})/common.sh
 
 tools/gn gen ${OUT_PATH} --args="${PERFETTO_TEST_GN_ARGS}" --check
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/configs/camera.cfg b/test/configs/camera.cfg
index 8677434..9bc76f5 100644
--- a/test/configs/camera.cfg
+++ b/test/configs/camera.cfg
@@ -42,8 +42,8 @@
       atrace_categories: "ss"
 
       # RSS and ION buffer events:
+      ftrace_events: "mm_event/mm_event_record"
       ftrace_events: "kmem/rss_stat"
-      ftrace_events: "kmem/mm_event"
       ftrace_events: "kmem/ion_heap_grow"
       ftrace_events: "kmem/ion_heap_shrink"
     }
diff --git a/test/configs/traced_perf.cfg b/test/configs/traced_perf.cfg
new file mode 100644
index 0000000..3334a4b
--- /dev/null
+++ b/test/configs/traced_perf.cfg
@@ -0,0 +1,26 @@
+buffers {
+  size_kb: 10240
+  fill_policy: RING_BUFFER
+}
+
+data_sources {
+  config {
+    name: "linux.perf"
+    perf_event_config {
+      all_cpus: true
+      sampling_frequency: 10
+    }
+  }
+}
+
+data_sources {
+  config {
+    name: "linux.process_stats"
+    target_buffer: 0
+    process_stats_config {
+      proc_stats_poll_ms: 100
+    }
+  }
+}
+
+duration_ms: 60000
diff --git a/test/cts/Android.bp b/test/cts/Android.bp
index 4871ebb..1692c56 100644
--- a/test/cts/Android.bp
+++ b/test/cts/Android.bp
@@ -3,8 +3,9 @@
   srcs: [
     "device_feature_test_cts.cc",
     "end_to_end_integrationtest_cts.cc",
-    "heapprofd_test_cts.cc",
     "heapprofd_java_test_cts.cc",
+    "heapprofd_test_cts.cc",
+    "traced_perf_test_cts.cc",
     "utils.cc",
     ":perfetto_protos_perfetto_config_cpp_gen",
   ],
@@ -18,13 +19,16 @@
     "perfetto_cts_deps",
     "perfetto_trace_protos",
   ],
+  whole_static_libs: [
+    "perfetto_gtest_logcat_printer",
+  ],
   shared_libs: [
     "libandroid",
     "liblog",
   ],
   test_suites: [
     "cts",
-    "vts",
+    "vts10",
     "general-tests",
   ],
   compile_multilib: "both",
diff --git a/test/cts/AndroidTest.xml b/test/cts/AndroidTest.xml
index aff1988..3d40593 100644
--- a/test/cts/AndroidTest.xml
+++ b/test/cts/AndroidTest.xml
@@ -34,7 +34,7 @@
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="setprop persist.heapprofd.enable 1" />
-        <option name="run-command" value="am start -n android.perfetto.producer/.ProducerActivity" />
+        <option name="run-command" value="setprop persist.traced_perf.enable 1" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.GTest" >
         <option name="native-test-device-path" value="/data/local/tmp" />
diff --git a/test/cts/BUILD.gn b/test/cts/BUILD.gn
index 84aba0c..84a7860 100644
--- a/test/cts/BUILD.gn
+++ b/test/cts/BUILD.gn
@@ -37,6 +37,7 @@
     "end_to_end_integrationtest_cts.cc",
     "heapprofd_java_test_cts.cc",
     "heapprofd_test_cts.cc",
+    "traced_perf_test_cts.cc",
     "utils.cc",
   ]
 }
diff --git a/test/cts/end_to_end_integrationtest_cts.cc b/test/cts/end_to_end_integrationtest_cts.cc
index 1a9407b..c1539c7 100644
--- a/test/cts/end_to_end_integrationtest_cts.cc
+++ b/test/cts/end_to_end_integrationtest_cts.cc
@@ -20,6 +20,7 @@
 
 #include "perfetto/tracing/core/data_source_config.h"
 #include "src/base/test/test_task_runner.h"
+#include "test/cts/utils.h"
 #include "test/test_helper.h"
 
 #include "protos/perfetto/config/test_config.gen.h"
@@ -43,6 +44,23 @@
 
     base::TestTaskRunner task_runner;
 
+    const std::string app_name = "android.perfetto.producer";
+    const std::string activity = "ProducerActivity";
+    if (IsAppRunning(app_name)) {
+      StopApp(app_name, "old.app.stopped", &task_runner);
+      task_runner.RunUntilCheckpoint("old.app.stopped");
+    }
+    StartAppActivity(app_name, activity, "target.app.running", &task_runner,
+                     /*delay_ms=*/100);
+    task_runner.RunUntilCheckpoint("target.app.running");
+
+    const std::string isolated_process_name =
+        "android.perfetto.producer:android.perfetto.producer."
+        "ProducerIsolatedService";
+    WaitForProcess(isolated_process_name, "isolated.service.running",
+                   &task_runner, 1000 /*ms*/);
+    task_runner.RunUntilCheckpoint("isolated.service.running");
+
     TestHelper helper(&task_runner);
     helper.ConnectConsumer();
     helper.WaitForConsumerConnect();
diff --git a/test/cts/heapprofd_java_test_cts.cc b/test/cts/heapprofd_java_test_cts.cc
index ae5a18a..e8d4701 100644
--- a/test/cts/heapprofd_java_test_cts.cc
+++ b/test/cts/heapprofd_java_test_cts.cc
@@ -34,6 +34,18 @@
 namespace perfetto {
 namespace {
 
+std::string RandomSessionName() {
+  std::random_device rd;
+  std::default_random_engine generator(rd());
+  std::uniform_int_distribution<char> distribution('a', 'z');
+
+  constexpr size_t kSessionNameLen = 20;
+  std::string result(kSessionNameLen, '\0');
+  for (size_t i = 0; i < kSessionNameLen; ++i)
+    result[i] = distribution(generator);
+  return result;
+}
+
 std::vector<protos::gen::TracePacket> ProfileRuntime(std::string app_name) {
   base::TestTaskRunner task_runner;
 
@@ -42,10 +54,9 @@
     StopApp(app_name, "old.app.stopped", &task_runner);
     task_runner.RunUntilCheckpoint("old.app.stopped", 1000 /*ms*/);
   }
-  StartAppActivity(app_name, "target.app.running", &task_runner,
+  StartAppActivity(app_name, "MainActivity", "target.app.running", &task_runner,
                    /*delay_ms=*/100);
   task_runner.RunUntilCheckpoint("target.app.running", 1000 /*ms*/);
-  // TODO(b/143467832): Remove this workaround.
   // If we try to dump too early in app initialization, we sometimes deadlock.
   sleep(1);
 
@@ -57,6 +68,7 @@
   TraceConfig trace_config;
   trace_config.add_buffers()->set_size_kb(20 * 1024);
   trace_config.set_duration_ms(6000);
+  trace_config.set_unique_session_name(RandomSessionName().c_str());
 
   auto* ds_config = trace_config.add_data_sources()->mutable_config();
   ds_config->set_name("android.java_hprof");
@@ -71,6 +83,7 @@
   helper.WaitForTracingDisabled(10000 /*ms*/);
   helper.ReadData();
   helper.WaitForReadData();
+  PERFETTO_CHECK(IsAppRunning(app_name));
   StopApp(app_name, "new.app.stopped", &task_runner);
   task_runner.RunUntilCheckpoint("new.app.stopped", 1000 /*ms*/);
   return helper.trace();
diff --git a/test/cts/heapprofd_test_cts.cc b/test/cts/heapprofd_test_cts.cc
index 739f303..05161b5 100644
--- a/test/cts/heapprofd_test_cts.cc
+++ b/test/cts/heapprofd_test_cts.cc
@@ -1,4 +1,3 @@
-
 /*
  * Copyright (C) 2019 The Android Open Source Project
  *
@@ -20,6 +19,8 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 
+#include <random>
+
 #include "perfetto/base/logging.h"
 #include "perfetto/tracing/core/data_source_config.h"
 #include "src/base/test/test_task_runner.h"
@@ -44,7 +45,21 @@
 static_assert(kExpectedIndividualAllocSz > kTestSamplingInterval,
               "kTestSamplingInterval invalid");
 
-std::vector<protos::gen::TracePacket> ProfileRuntime(std::string app_name) {
+std::string RandomSessionName() {
+  std::random_device rd;
+  std::default_random_engine generator(rd());
+  std::uniform_int_distribution<char> distribution('a', 'z');
+
+  constexpr size_t kSessionNameLen = 20;
+  std::string result(kSessionNameLen, '\0');
+  for (size_t i = 0; i < kSessionNameLen; ++i)
+    result[i] = distribution(generator);
+  return result;
+}
+
+std::vector<protos::gen::TracePacket> ProfileRuntime(
+    const std::string& app_name,
+    const bool enable_extra_guardrails = false) {
   base::TestTaskRunner task_runner;
 
   // (re)start the target app's main activity
@@ -52,7 +67,7 @@
     StopApp(app_name, "old.app.stopped", &task_runner);
     task_runner.RunUntilCheckpoint("old.app.stopped", 1000 /*ms*/);
   }
-  StartAppActivity(app_name, "target.app.running", &task_runner,
+  StartAppActivity(app_name, "MainActivity", "target.app.running", &task_runner,
                    /*delay_ms=*/100);
   task_runner.RunUntilCheckpoint("target.app.running", 1000 /*ms*/);
 
@@ -64,6 +79,8 @@
   TraceConfig trace_config;
   trace_config.add_buffers()->set_size_kb(10 * 1024);
   trace_config.set_duration_ms(4000);
+  trace_config.set_enable_extra_guardrails(enable_extra_guardrails);
+  trace_config.set_unique_session_name(RandomSessionName().c_str());
 
   auto* ds_config = trace_config.add_data_sources()->mutable_config();
   ds_config->set_name("android.heapprofd");
@@ -85,7 +102,9 @@
   return helper.trace();
 }
 
-std::vector<protos::gen::TracePacket> ProfileStartup(std::string app_name) {
+std::vector<protos::gen::TracePacket> ProfileStartup(
+    const std::string& app_name,
+    const bool enable_extra_guardrails = false) {
   base::TestTaskRunner task_runner;
 
   if (IsAppRunning(app_name)) {
@@ -101,6 +120,8 @@
   TraceConfig trace_config;
   trace_config.add_buffers()->set_size_kb(10 * 1024);
   trace_config.set_duration_ms(4000);
+  trace_config.set_enable_extra_guardrails(enable_extra_guardrails);
+  trace_config.set_unique_session_name(RandomSessionName().c_str());
 
   auto* ds_config = trace_config.add_data_sources()->mutable_config();
   ds_config->set_name("android.heapprofd");
@@ -117,7 +138,7 @@
   helper.StartTracing(trace_config);
 
   // start app
-  StartAppActivity(app_name, "target.app.running", &task_runner,
+  StartAppActivity(app_name, "MainActivity", "target.app.running", &task_runner,
                    /*delay_ms=*/100);
   task_runner.RunUntilCheckpoint("target.app.running", 2000 /*ms*/);
 
@@ -129,7 +150,7 @@
 }
 
 void AssertExpectedAllocationsPresent(
-    std::vector<protos::gen::TracePacket> packets) {
+    const std::vector<protos::gen::TracePacket>& packets) {
   ASSERT_GT(packets.size(), 0u);
 
   // TODO(rsavitski): assert particular stack frames once we clarify the
@@ -137,8 +158,10 @@
   // Until then, look for an allocation that is a multiple of the expected
   // allocation size.
   bool found_alloc = false;
+  bool found_proc_dump = false;
   for (const auto& packet : packets) {
     for (const auto& proc_dump : packet.profile_packet().process_dumps()) {
+      found_proc_dump = true;
       for (const auto& sample : proc_dump.samples()) {
         if (sample.self_allocated() > 0 &&
             sample.self_allocated() % kExpectedIndividualAllocSz == 0) {
@@ -151,10 +174,12 @@
       }
     }
   }
+  ASSERT_TRUE(found_proc_dump);
   ASSERT_TRUE(found_alloc);
 }
 
-void AssertNoProfileContents(std::vector<protos::gen::TracePacket> packets) {
+void AssertNoProfileContents(
+    const std::vector<protos::gen::TracePacket>& packets) {
   // If profile packets are present, they must be empty.
   for (const auto& packet : packets) {
     ASSERT_EQ(packet.profile_packet().process_dumps_size(), 0);
@@ -189,6 +214,30 @@
   StopApp(app_name);
 }
 
+TEST(HeapprofdCtsTest, ProfileableAppRuntimeExtraGuardrails) {
+  std::string app_name = "android.perfetto.cts.app.profileable";
+  const auto& packets = ProfileRuntime(app_name,
+                                       /*enable_extra_guardrails=*/true);
+
+  if (IsUserBuild())
+    AssertNoProfileContents(packets);
+  else
+    AssertExpectedAllocationsPresent(packets);
+  StopApp(app_name);
+}
+
+TEST(HeapprofdCtsTest, ProfileableAppStartupExtraGuardrails) {
+  std::string app_name = "android.perfetto.cts.app.profileable";
+  const auto& packets = ProfileStartup(app_name,
+                                       /*enable_extra_guardrails=*/
+                                       true);
+  if (IsUserBuild())
+    AssertNoProfileContents(packets);
+  else
+    AssertExpectedAllocationsPresent(packets);
+  StopApp(app_name);
+}
+
 TEST(HeapprofdCtsTest, ReleaseAppRuntime) {
   std::string app_name = "android.perfetto.cts.app.release";
   const auto& packets = ProfileRuntime(app_name);
diff --git a/test/cts/producer/Android.bp b/test/cts/producer/Android.bp
index 5458af0..bda36bd 100644
--- a/test/cts/producer/Android.bp
+++ b/test/cts/producer/Android.bp
@@ -17,7 +17,7 @@
     // tag this module as a cts test artifact
     test_suites: [
         "cts",
-        "vts",
+        "vts10",
         "general-tests",
     ],
 
@@ -28,4 +28,5 @@
         "libperfettocts_jni",
         "libnativehelper_compat_libc++",
     ],
+    jni_uses_platform_apis: true,
 }
diff --git a/test/cts/producer/jni/fake_producer_jni.cc b/test/cts/producer/jni/fake_producer_jni.cc
index ab587a8..a3d51f6 100644
--- a/test/cts/producer/jni/fake_producer_jni.cc
+++ b/test/cts/producer/jni/fake_producer_jni.cc
@@ -56,8 +56,9 @@
     lock.unlock();
   });
 
-  FakeProducer producer(name);
-  producer.Connect(GetProducerSocket(), &task_runner, [] {}, [] {});
+  FakeProducer producer(name, &task_runner);
+  producer.Connect(
+      GetProducerSocket(), [] {}, [] {}, [] {});
   task_runner.Run();
 
   // Cleanup the task runner again to remove outside visibilty so we can
diff --git a/test/cts/heapprofd_test_apps/Android.bp b/test/cts/test_apps/Android.bp
similarity index 87%
rename from test/cts/heapprofd_test_apps/Android.bp
rename to test/cts/test_apps/Android.bp
index bc2b323..7b9891b 100644
--- a/test/cts/heapprofd_test_apps/Android.bp
+++ b/test/cts/test_apps/Android.bp
@@ -17,7 +17,7 @@
     // tag this module as a cts test artifact
     test_suites: [
         "cts",
-        "vts",
+        "vts10",
         "general-tests",
     ],
 
@@ -27,9 +27,10 @@
     srcs: ["src/**/*.java"],
     sdk_version: "current",
     jni_libs: [
-        "libperfettocts_heapprofdtarget",
+        "libperfettocts_native",
         "libnativehelper_compat_libc++",
     ],
+    jni_uses_platform_apis: true,
 }
 
 android_test_helper_app {
@@ -37,7 +38,7 @@
     // tag this module as a cts test artifact
     test_suites: [
         "cts",
-        "vts",
+        "vts10",
         "general-tests",
     ],
 
@@ -47,9 +48,10 @@
     srcs: ["src/**/*.java"],
     sdk_version: "current",
     jni_libs: [
-        "libperfettocts_heapprofdtarget",
+        "libperfettocts_native",
         "libnativehelper_compat_libc++",
     ],
+    jni_uses_platform_apis: true,
 }
 
 android_test_helper_app {
@@ -57,7 +59,7 @@
     // tag this module as a cts test artifact
     test_suites: [
         "cts",
-        "vts",
+        "vts10",
         "general-tests",
     ],
 
@@ -67,7 +69,8 @@
     srcs: ["src/**/*.java"],
     sdk_version: "current",
     jni_libs: [
-        "libperfettocts_heapprofdtarget",
+        "libperfettocts_native",
         "libnativehelper_compat_libc++",
     ],
+    jni_uses_platform_apis: true,
 }
diff --git a/test/cts/heapprofd_test_apps/AndroidManifest_debuggable.xml b/test/cts/test_apps/AndroidManifest_debuggable.xml
similarity index 71%
rename from test/cts/heapprofd_test_apps/AndroidManifest_debuggable.xml
rename to test/cts/test_apps/AndroidManifest_debuggable.xml
index 999541e..c85ab05 100755
--- a/test/cts/heapprofd_test_apps/AndroidManifest_debuggable.xml
+++ b/test/cts/test_apps/AndroidManifest_debuggable.xml
@@ -31,6 +31,18 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity-alias>
+        <activity
+          android:name="android.perfetto.cts.app.BusyWaitActivity"
+          android:exported="false">
+        </activity>
+        <activity-alias
+          android:name="android.perfetto.cts.app.debuggable.BusyWaitActivity"
+          android:targetActivity="android.perfetto.cts.app.BusyWaitActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity-alias>
     </application>
 </manifest>
 
diff --git a/test/cts/heapprofd_test_apps/AndroidManifest_profileable.xml b/test/cts/test_apps/AndroidManifest_profileable.xml
similarity index 71%
rename from test/cts/heapprofd_test_apps/AndroidManifest_profileable.xml
rename to test/cts/test_apps/AndroidManifest_profileable.xml
index 99ceda2..129e922 100755
--- a/test/cts/heapprofd_test_apps/AndroidManifest_profileable.xml
+++ b/test/cts/test_apps/AndroidManifest_profileable.xml
@@ -32,6 +32,18 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity-alias>
+        <activity
+          android:name="android.perfetto.cts.app.BusyWaitActivity"
+          android:exported="false">
+        </activity>
+        <activity-alias
+          android:name="android.perfetto.cts.app.profileable.BusyWaitActivity"
+          android:targetActivity="android.perfetto.cts.app.BusyWaitActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity-alias>
     </application>
 </manifest>
 
diff --git a/test/cts/heapprofd_test_apps/AndroidManifest_release.xml b/test/cts/test_apps/AndroidManifest_release.xml
similarity index 71%
rename from test/cts/heapprofd_test_apps/AndroidManifest_release.xml
rename to test/cts/test_apps/AndroidManifest_release.xml
index b2dfd25..5b64a94 100755
--- a/test/cts/heapprofd_test_apps/AndroidManifest_release.xml
+++ b/test/cts/test_apps/AndroidManifest_release.xml
@@ -31,6 +31,18 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity-alias>
+        <activity
+          android:name="android.perfetto.cts.app.BusyWaitActivity"
+          android:exported="false">
+        </activity>
+        <activity-alias
+          android:name="android.perfetto.cts.app.release.BusyWaitActivity"
+          android:targetActivity="android.perfetto.cts.app.BusyWaitActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity-alias>
     </application>
 </manifest>
 
diff --git a/test/cts/heapprofd_test_apps/jni/Android.bp b/test/cts/test_apps/jni/Android.bp
similarity index 95%
rename from test/cts/heapprofd_test_apps/jni/Android.bp
rename to test/cts/test_apps/jni/Android.bp
index de090d2..3f50e72 100644
--- a/test/cts/heapprofd_test_apps/jni/Android.bp
+++ b/test/cts/test_apps/jni/Android.bp
@@ -14,7 +14,7 @@
 // limitations under the License.
 
 cc_library_shared {
-  name: "libperfettocts_heapprofdtarget",
+  name: "libperfettocts_native",
   srcs: [
     "target.cc",
   ],
diff --git a/test/cts/heapprofd_test_apps/jni/target.cc b/test/cts/test_apps/jni/target.cc
similarity index 77%
rename from test/cts/heapprofd_test_apps/jni/target.cc
rename to test/cts/test_apps/jni/target.cc
index 6bedf84..8f847c4 100644
--- a/test/cts/heapprofd_test_apps/jni/target.cc
+++ b/test/cts/test_apps/jni/target.cc
@@ -36,9 +36,21 @@
   }
 }
 
+// Runs continuously as a target for the sampling perf profiler tests.
+__attribute__((noreturn)) void perfetto_busy_wait() {
+  for (volatile unsigned i = 0;; i++) {
+  }
+}
+
 }  // namespace
 
 extern "C" JNIEXPORT void JNICALL
 Java_android_perfetto_cts_app_MainActivity_runNative(JNIEnv*, jclass) {
   perfetto_test_allocations();
 }
+
+extern "C" JNIEXPORT void JNICALL
+Java_android_perfetto_cts_app_BusyWaitActivity_runNativeBusyWait(JNIEnv*,
+                                                                 jclass) {
+  perfetto_busy_wait();
+}
diff --git a/test/cts/heapprofd_test_apps/src/android/perfetto/cts/app/MainActivity.java b/test/cts/test_apps/src/android/perfetto/cts/app/BusyWaitActivity.java
similarity index 82%
copy from test/cts/heapprofd_test_apps/src/android/perfetto/cts/app/MainActivity.java
copy to test/cts/test_apps/src/android/perfetto/cts/app/BusyWaitActivity.java
index 0e26bfc..689c98a 100644
--- a/test/cts/heapprofd_test_apps/src/android/perfetto/cts/app/MainActivity.java
+++ b/test/cts/test_apps/src/android/perfetto/cts/app/BusyWaitActivity.java
@@ -19,9 +19,9 @@
 import android.app.Activity;
 import android.os.Bundle;
 
-public class MainActivity extends Activity {
+public class BusyWaitActivity extends Activity {
     static {
-        System.loadLibrary("perfettocts_heapprofdtarget");
+        System.loadLibrary("perfettocts_native");
     }
 
     @Override
@@ -31,14 +31,13 @@
         new Thread(new Runnable() {
             public void run() {
                 try {
-                    runNative();
+                    runNativeBusyWait();
                 } catch (Exception ex) {
                     ex.printStackTrace();
                 }
             }
-        })
-                .start();
+        }).start();
     }
 
-    private static native void runNative();
+    private static native void runNativeBusyWait();
 }
diff --git a/test/cts/heapprofd_test_apps/src/android/perfetto/cts/app/MainActivity.java b/test/cts/test_apps/src/android/perfetto/cts/app/MainActivity.java
similarity index 92%
rename from test/cts/heapprofd_test_apps/src/android/perfetto/cts/app/MainActivity.java
rename to test/cts/test_apps/src/android/perfetto/cts/app/MainActivity.java
index 0e26bfc..0cabd16 100644
--- a/test/cts/heapprofd_test_apps/src/android/perfetto/cts/app/MainActivity.java
+++ b/test/cts/test_apps/src/android/perfetto/cts/app/MainActivity.java
@@ -21,7 +21,7 @@
 
 public class MainActivity extends Activity {
     static {
-        System.loadLibrary("perfettocts_heapprofdtarget");
+        System.loadLibrary("perfettocts_native");
     }
 
     @Override
@@ -36,8 +36,7 @@
                     ex.printStackTrace();
                 }
             }
-        })
-                .start();
+        }).start();
     }
 
     private static native void runNative();
diff --git a/test/cts/traced_perf_test_cts.cc b/test/cts/traced_perf_test_cts.cc
new file mode 100644
index 0000000..bbc0add
--- /dev/null
+++ b/test/cts/traced_perf_test_cts.cc
@@ -0,0 +1,191 @@
+/*
+ * 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 <stdlib.h>
+#include <sys/system_properties.h>
+#include <sys/types.h>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/tracing/core/data_source_config.h"
+#include "src/base/test/test_task_runner.h"
+#include "test/cts/utils.h"
+#include "test/gtest_and_gmock.h"
+#include "test/test_helper.h"
+
+#include "protos/perfetto/config/profiling/perf_event_config.gen.h"
+#include "protos/perfetto/trace/profiling/profile_common.gen.h"
+#include "protos/perfetto/trace/profiling/profile_packet.gen.h"
+#include "protos/perfetto/trace/trace_packet.gen.h"
+
+namespace perfetto {
+namespace {
+
+// Skip these tests if the device in question doesn't have the necessary kernel
+// LSM hooks in perf_event_open. This comes up when a device with an older
+// kernel upgrades to R.
+bool HasPerfLsmHooks() {
+  char buf[PROP_VALUE_MAX + 1] = {};
+  int ret = __system_property_get("sys.init.perf_lsm_hooks", buf);
+  PERFETTO_CHECK(ret >= 0);
+  return std::string(buf) == "1";
+}
+
+std::vector<protos::gen::TracePacket> ProfileSystemWide(std::string app_name) {
+  base::TestTaskRunner task_runner;
+
+  // (re)start the target app's main activity
+  if (IsAppRunning(app_name)) {
+    StopApp(app_name, "old.app.stopped", &task_runner);
+    task_runner.RunUntilCheckpoint("old.app.stopped", 1000 /*ms*/);
+  }
+  StartAppActivity(app_name, "BusyWaitActivity", "target.app.running",
+                   &task_runner,
+                   /*delay_ms=*/100);
+  task_runner.RunUntilCheckpoint("target.app.running", 1000 /*ms*/);
+
+  // set up tracing
+  TestHelper helper(&task_runner);
+  helper.ConnectConsumer();
+  helper.WaitForConsumerConnect();
+
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(20 * 1024);
+  trace_config.set_duration_ms(3000);
+  trace_config.set_data_source_stop_timeout_ms(8000);
+
+  auto* ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("linux.perf");
+  ds_config->set_target_buffer(0);
+
+  protos::gen::PerfEventConfig perf_config;
+
+  perf_config.set_all_cpus(true);
+  perf_config.set_sampling_frequency(10);  // Hz
+  ds_config->set_perf_event_config_raw(perf_config.SerializeAsString());
+
+  // start tracing
+  helper.StartTracing(trace_config);
+  helper.WaitForTracingDisabled(15000 /*ms*/);
+  helper.ReadData();
+  helper.WaitForReadData();
+
+  return helper.trace();
+}
+
+void AssertHasSampledStacksForPid(std::vector<protos::gen::TracePacket> packets,
+                                  int pid) {
+  uint32_t target_pid = static_cast<uint32_t>(pid);
+  ASSERT_GT(packets.size(), 0u);
+
+  int total_perf_packets = 0;
+  int lost_records_packets = 0;
+  int full_samples = 0;
+  int target_samples = 0;
+  int target_skipped_samples = 0;
+  for (const auto& packet : packets) {
+    if (!packet.has_perf_sample())
+      continue;
+
+    total_perf_packets++;
+    EXPECT_GT(packet.timestamp(), 0u) << "all packets should have a timestamp";
+    const auto& sample = packet.perf_sample();
+    if (sample.has_kernel_records_lost()) {
+      lost_records_packets++;
+      continue;
+    }
+    if (sample.has_sample_skipped_reason()) {
+      if (sample.pid() == target_pid)
+        target_skipped_samples++;
+      continue;
+    }
+
+    full_samples++;
+    EXPECT_GT(sample.tid(), 0u);
+    EXPECT_GT(sample.callstack_iid(), 0u);
+
+    if (sample.pid() == target_pid)
+      target_samples++;
+  }
+
+  EXPECT_GT(target_samples, 0)
+      << "target_pid: " << target_pid << ", packets.size(): " << packets.size()
+      << ", total_perf_packets: " << total_perf_packets
+      << ", full_samples: " << full_samples
+      << ", lost_records_packets: " << lost_records_packets
+      << ", target_skipped_samples: " << target_skipped_samples << "\n";
+}
+
+void AssertNoStacksForPid(std::vector<protos::gen::TracePacket> packets,
+                          int pid) {
+  uint32_t target_pid = static_cast<uint32_t>(pid);
+  // The process can still be sampled, but the stacks should be discarded
+  // without unwinding.
+  for (const auto& packet : packets) {
+    if (packet.perf_sample().pid() == target_pid) {
+      EXPECT_EQ(packet.perf_sample().callstack_iid(), 0u);
+      EXPECT_TRUE(packet.perf_sample().has_sample_skipped_reason());
+    }
+  }
+}
+
+TEST(TracedPerfCtsTest, SystemWideDebuggableApp) {
+  if (!HasPerfLsmHooks())
+    GTEST_SKIP() << "skipped due to lack of perf_event_open LSM hooks";
+
+  std::string app_name = "android.perfetto.cts.app.debuggable";
+  const auto& packets = ProfileSystemWide(app_name);
+  int app_pid = PidForProcessName(app_name);
+  ASSERT_GT(app_pid, 0) << "failed to find pid for target process";
+
+  AssertHasSampledStacksForPid(packets, app_pid);
+  PERFETTO_CHECK(IsAppRunning(app_name));
+  StopApp(app_name);
+}
+
+TEST(TracedPerfCtsTest, SystemWideProfileableApp) {
+  if (!HasPerfLsmHooks())
+    GTEST_SKIP() << "skipped due to lack of perf_event_open LSM hooks";
+
+  std::string app_name = "android.perfetto.cts.app.profileable";
+  const auto& packets = ProfileSystemWide(app_name);
+  int app_pid = PidForProcessName(app_name);
+  ASSERT_GT(app_pid, 0) << "failed to find pid for target process";
+
+  AssertHasSampledStacksForPid(packets, app_pid);
+  PERFETTO_CHECK(IsAppRunning(app_name));
+  StopApp(app_name);
+}
+
+TEST(TracedPerfCtsTest, SystemWideReleaseApp) {
+  if (!HasPerfLsmHooks())
+    GTEST_SKIP() << "skipped due to lack of perf_event_open LSM hooks";
+
+  std::string app_name = "android.perfetto.cts.app.release";
+  const auto& packets = ProfileSystemWide(app_name);
+  int app_pid = PidForProcessName(app_name);
+  ASSERT_GT(app_pid, 0) << "failed to find pid for target process";
+
+  if (IsDebuggableBuild())
+    AssertHasSampledStacksForPid(packets, app_pid);
+  else
+    AssertNoStacksForPid(packets, app_pid);
+
+  PERFETTO_CHECK(IsAppRunning(app_name));
+  StopApp(app_name);
+}
+
+}  // namespace
+}  // namespace perfetto
diff --git a/test/cts/utils.cc b/test/cts/utils.cc
index 1407fa1..1dd9b1e 100644
--- a/test/cts/utils.cc
+++ b/test/cts/utils.cc
@@ -20,6 +20,7 @@
 #include <sys/system_properties.h>
 
 #include "perfetto/base/logging.h"
+#include "perfetto/ext/base/file_utils.h"
 #include "test/gtest_and_gmock.h"
 
 namespace perfetto {
@@ -48,15 +49,19 @@
   char buf[PROP_VALUE_MAX + 1] = {};
   int ret = __system_property_get("ro.debuggable", buf);
   PERFETTO_CHECK(ret >= 0);
-  std::string debuggable(buf);
-  if (debuggable == "1")
-    return true;
-  return false;
+  return std::string(buf) == "1";
+}
+
+bool IsUserBuild() {
+  char buf[PROP_VALUE_MAX + 1] = {};
+  int ret = __system_property_get("ro.build.type", buf);
+  PERFETTO_CHECK(ret >= 0);
+  return std::string(buf) == "user";
 }
 
 // note: cannot use gtest macros due to return type
 bool IsAppRunning(const std::string& name) {
-  std::string cmd = "pgrep -f " + name;
+  std::string cmd = "pgrep -f ^" + name + "$";
   int retcode = system(cmd.c_str());
   PERFETTO_CHECK(retcode >= 0);
   int exit_status = WEXITSTATUS(retcode);
@@ -67,22 +72,46 @@
   PERFETTO_FATAL("unexpected exit status from system(pgrep): %d", exit_status);
 }
 
-void StartAppActivity(const std::string& app_name,
-                      const std::string& checkpoint_name,
-                      base::TestTaskRunner* task_runner,
-                      int delay_ms) {
-  std::string start_cmd = "am start " + app_name + "/.MainActivity";
-  int status = system(start_cmd.c_str());
-  ASSERT_TRUE(status >= 0 && WEXITSTATUS(status) == 0) << "status: " << status;
+int PidForProcessName(const std::string& name) {
+  std::string cmd = "pgrep -f ^" + name + "$";
+  FILE* fp = popen(cmd.c_str(), "re");
+  if (!fp)
+    return -1;
 
+  std::string out;
+  base::ReadFileStream(fp, &out);
+  pclose(fp);
+
+  char* endptr = nullptr;
+  int pid = static_cast<int>(strtol(out.c_str(), &endptr, 10));
+  if (*endptr != '\0' && *endptr != '\n')
+    return -1;
+  return pid;
+}
+
+void WaitForProcess(const std::string& process,
+                    const std::string& checkpoint_name,
+                    base::TestTaskRunner* task_runner,
+                    uint32_t delay_ms) {
   bool desired_run_state = true;
   const auto checkpoint = task_runner->CreateCheckpoint(checkpoint_name);
   task_runner->PostDelayedTask(
-      [desired_run_state, task_runner, app_name, checkpoint] {
-        PollRunState(desired_run_state, task_runner, app_name,
+      [desired_run_state, task_runner, process, checkpoint] {
+        PollRunState(desired_run_state, task_runner, process,
                      std::move(checkpoint));
       },
-      static_cast<uint32_t>(delay_ms));
+      delay_ms);
+}
+
+void StartAppActivity(const std::string& app_name,
+                      const std::string& activity_name,
+                      const std::string& checkpoint_name,
+                      base::TestTaskRunner* task_runner,
+                      uint32_t delay_ms) {
+  std::string start_cmd = "am start " + app_name + "/." + activity_name;
+  int status = system(start_cmd.c_str());
+  ASSERT_TRUE(status >= 0 && WEXITSTATUS(status) == 0) << "status: " << status;
+  WaitForProcess(app_name, checkpoint_name, task_runner, delay_ms);
 }
 
 void StopApp(const std::string& app_name,
@@ -100,7 +129,7 @@
   });
 }
 
-void StopApp(std::string app_name) {
+void StopApp(const std::string& app_name) {
   std::string stop_cmd = "am force-stop " + app_name;
   system(stop_cmd.c_str());
 }
diff --git a/test/cts/utils.h b/test/cts/utils.h
index 1c8ed26..724c367 100644
--- a/test/cts/utils.h
+++ b/test/cts/utils.h
@@ -24,19 +24,29 @@
 namespace perfetto {
 
 bool IsDebuggableBuild();
+bool IsUserBuild();
 
 bool IsAppRunning(const std::string& name);
 
+// returns -1 if the process wasn't found
+int PidForProcessName(const std::string& name);
+
+void WaitForProcess(const std::string& process,
+                    const std::string& checkpoint_name,
+                    base::TestTaskRunner* task_runner,
+                    uint32_t delay_ms = 1);
+
 void StartAppActivity(const std::string& app_name,
+                      const std::string& activity_name,
                       const std::string& checkpoint_name,
                       base::TestTaskRunner* task_runner,
-                      int delay_ms = 1);
+                      uint32_t delay_ms = 1);
 
 void StopApp(const std::string& app_name,
              const std::string& checkpoint_name,
              base::TestTaskRunner* task_runner);
 
-void StopApp(std::string app_name);
+void StopApp(const std::string& app_name);
 
 }  // namespace perfetto
 
diff --git a/test/end_to_end_integrationtest.cc b/test/end_to_end_integrationtest.cc
index 52947df..7d001d6 100644
--- a/test/end_to_end_integrationtest.cc
+++ b/test/end_to_end_integrationtest.cc
@@ -26,12 +26,19 @@
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/pipe.h"
+#include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/base/subprocess.h"
 #include "perfetto/ext/base/temp_file.h"
+#include "perfetto/ext/ipc/basic_types.h"
 #include "perfetto/ext/traced/traced.h"
+#include "perfetto/ext/tracing/core/commit_data_request.h"
 #include "perfetto/ext/tracing/core/trace_packet.h"
 #include "perfetto/ext/tracing/ipc/default_socket.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
+#include "perfetto/tracing/core/tracing_service_state.h"
 #include "src/base/test/test_task_runner.h"
+#include "src/base/test/utils.h"
 #include "src/traced/probes/ftrace/ftrace_controller.h"
 #include "src/traced/probes/ftrace/ftrace_procfs.h"
 #include "test/gtest_and_gmock.h"
@@ -55,8 +62,11 @@
 namespace {
 
 using ::testing::ContainsRegex;
+using ::testing::ElementsAreArray;
 using ::testing::HasSubstr;
 
+constexpr size_t kBuiltinPackets = 8;
+
 std::string RandomTraceFileName() {
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
   constexpr char kSysTmpPath[] = "/data/misc/perfetto-traces";
@@ -82,181 +92,90 @@
 // any necessary threads) in the parent process is complete.
 class Exec {
  public:
-  // Starts the forked process that was created. If not null then |stderr_out
-  // will contain the std::cerr output of the process.
+  // Starts the forked process that was created. If not null then |stderr_out|
+  // will contain the stderr of the process.
   int Run(std::string* stderr_out = nullptr) {
     // We can't be the child process.
-    PERFETTO_CHECK(pid_ != 0);
+    PERFETTO_CHECK(getpid() != subprocess_.pid());
+    // Will cause the entrypoint to continue.
+    PERFETTO_CHECK(write(*sync_pipe_.wr, "1", 1) == 1);
+    sync_pipe_.wr.reset();
+    subprocess_.Wait();
 
-    // Send some random bytes so the child process knows the service is up and
-    // it can connect and execute.
-    PERFETTO_CHECK(PERFETTO_EINTR(write(*start_pipe_.wr, "42", 2)) ==
-                   static_cast<ssize_t>(2));
-    start_pipe_.wr.reset();
-
-    // Setup a large enough buffer and read all of stderr (until the process
-    // closes the err_pipe on process exit).
-    std::string stderr_str = std::string(1024 * 1024, '\0');
-    ssize_t rsize = 0;
-    size_t stderr_pos = 0;
-    while (stderr_pos < stderr_str.size()) {
-      rsize = PERFETTO_EINTR(read(*err_pipe_.rd, &stderr_str[stderr_pos],
-                                  stderr_str.size() - stderr_pos - 1));
-      if (rsize <= 0)
-        break;
-      stderr_pos += static_cast<size_t>(rsize);
-    }
-    stderr_str.resize(stderr_pos);
-
-    // Either output the stderr_out to the provided variable or for the record
-    // it into the info logs.
     if (stderr_out) {
-      *stderr_out = stderr_str;
+      *stderr_out = std::move(subprocess_.output());
     } else {
-      PERFETTO_LOG("Child proc %d exited with stderr: \"%s\"", pid_,
-                   stderr_str.c_str());
+      PERFETTO_LOG("Child proc %d exited with stderr: \"%s\"",
+                   subprocess_.pid(), subprocess_.output().c_str());
     }
-
-    int status = 1;
-    PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid_, &status, 0)) == pid_);
-    int exit_code;
-    if (WIFEXITED(status)) {
-      exit_code = WEXITSTATUS(status);
-    } else if (WIFSIGNALED(status)) {
-      exit_code = -(WTERMSIG(status));
-      PERFETTO_CHECK(exit_code < 0);
-    } else {
-      PERFETTO_FATAL("Unexpected exit status: %d", status);
-    }
-    return exit_code;
+    return subprocess_.returncode();
   }
 
  private:
-  Exec(pid_t pid, base::Pipe err, base::Pipe start)
-      : pid_(pid), err_pipe_(std::move(err)), start_pipe_(std::move(start)) {}
+  Exec(const std::string& argv0,
+       std::initializer_list<std::string> args,
+       std::string input = "") {
+    subprocess_.args.stderr_mode = base::Subprocess::kBuffer;
+    subprocess_.args.stdout_mode = base::Subprocess::kDevNull;
+    subprocess_.args.input = input;
 
-  static Exec Create(const std::string& argv0,
-                     std::initializer_list<std::string> args,
-                     std::string input = "") {
-    if (argv0 != "perfetto" && argv0 != "trigger_perfetto") {
-      PERFETTO_FATAL(
-          "Received argv0: \"%s\" which isn't supported. Supported binaries "
-          "are \"perfetto\" or \"trigger_perfetto\".",
-          argv0.c_str());
-    }
-
-    // |in_pipe| == std::cin, |err_pipe| == std::cerr for the process we're
-    // about to fork. |start_pipe| is used to block the process so we can hold
-    // it until we're ready (the service has started up).
-    base::Pipe in_pipe = base::Pipe::Create();
-    base::Pipe err_pipe = base::Pipe::Create();
-    base::Pipe start_pipe = base::Pipe::Create();
-
-    pid_t pid = fork();
-    PERFETTO_CHECK(pid >= 0);
-    if (pid == 0) {
-      // Child process, we need to block the child process until we've been
-      // signaled on the |start_pipe|.
-      std::string junk = std::string(4, '\0');
-      start_pipe.wr.reset();
-      ssize_t rsize = 0;
-      rsize = PERFETTO_EINTR(read(*start_pipe.rd, &junk[0], junk.size() - 1));
-      PERFETTO_CHECK(rsize >= 0);
-      start_pipe.rd.reset();
-
-      // We've been signalled to start so execute in a sub function.
-      _exit(RunChild(argv0, std::move(args), std::move(in_pipe),
-                     std::move(err_pipe)));
-    } else {
-      // Parent, we don't need to write to the childs std::cerr nor do we need
-      // to read the start_pipe.
-      err_pipe.wr.reset();
-      start_pipe.rd.reset();
-
-      // This is generally an unsafe pattern because the child process might
-      // be blocked on stdout and stall the stdin reads. It's pragmatically
-      // okay for our test cases because stdin is not expected to exceed the
-      // pipe buffer.
-      //
-      // We need to write this now up front (rather than in Run(), because in
-      // some tests we create multiple Exec classes, and if we don't close the
-      // input pipe up front then future Exec's will have a reference and the
-      // pipe won't close properly.
-      PERFETTO_CHECK(input.size() <= base::kPageSize);
-      PERFETTO_CHECK(
-          PERFETTO_EINTR(write(*in_pipe.wr, input.data(), input.size())) ==
-          static_cast<ssize_t>(input.size()));
-      in_pipe.wr.reset();
-      // Close the input pipe only after the write so we don't get an EPIPE
-      // signal in the cases when the child process earlies out without
-      // reading stdin.
-      in_pipe.rd.reset();
-
-      return Exec(pid, std::move(err_pipe), std::move(start_pipe));
-    }
-  }
-
-  // Wrapper to contain all the work the child process needs to do.
-  static int RunChild(const std::string& argv0,
-                      std::initializer_list<std::string> args,
-                      base::Pipe in_pipe,
-                      base::Pipe err_pipe) {
-    // This sets up the char** argv buffer we're going to provide to the main
-    // function for |argv0| binary.
-    std::vector<char> argv_buffer;
-    std::vector<size_t> argv_offsets;
-    std::vector<char*> argv;
-    argv_offsets.push_back(0);
-
-    argv_buffer.insert(argv_buffer.end(), argv0.begin(), argv0.end());
-    argv_buffer.push_back('\0');
-
-    for (const std::string& arg : args) {
-      argv_offsets.push_back(argv_buffer.size());
-      argv_buffer.insert(argv_buffer.end(), arg.begin(), arg.end());
-      argv_buffer.push_back('\0');
-    }
-
-    for (size_t off : argv_offsets)
-      argv.push_back(&argv_buffer[off]);
-    argv.push_back(nullptr);
-
-    // We aren't reading std::cerr nor writing to std::cin.
-    err_pipe.rd.reset();
-    in_pipe.wr.reset();
-
-    // This makes it so the binaries below will correctly write their std::cin
-    // and std::cerr to the right pipes.
-    int devnull = open("/dev/null", O_RDWR);
-    PERFETTO_CHECK(devnull >= 0);
-    PERFETTO_CHECK(dup2(*in_pipe.rd, STDIN_FILENO) != -1);
-    PERFETTO_CHECK(dup2(devnull, STDOUT_FILENO) != -1);
-    PERFETTO_CHECK(dup2(*err_pipe.wr, STDERR_FILENO) != -1);
 #if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
-    setenv("PERFETTO_CONSUMER_SOCK_NAME", TestHelper::GetConsumerSocketName(),
-           1);
-    setenv("PERFETTO_PRODUCER_SOCK_NAME", TestHelper::GetProducerSocketName(),
-           1);
-    if (argv0 == "perfetto") {
-      return PerfettoCmdMain(static_cast<int>(argv.size() - 1), argv.data());
-    } else if (argv0 == "trigger_perfetto") {
-      return TriggerPerfettoMain(static_cast<int>(argv.size() - 1),
-                                 argv.data());
-    } else {
-      PERFETTO_FATAL("Unknown binary: %s", argv0.c_str());
-      return 4;
-    }
+    constexpr bool kUseSystemBinaries = false;
 #else
-    execv((std::string("/system/bin/") + argv0).c_str(), &argv[0]);
-    return 3;
+    constexpr bool kUseSystemBinaries = true;
 #endif
+
+    std::vector<std::string>& cmd = subprocess_.args.exec_cmd;
+    if (kUseSystemBinaries) {
+      cmd.push_back("/system/bin/" + argv0);
+      cmd.insert(cmd.end(), args.begin(), args.end());
+    } else {
+      subprocess_.args.env.push_back(
+          std::string("PERFETTO_PRODUCER_SOCK_NAME=") +
+          TestHelper::GetProducerSocketName());
+      subprocess_.args.env.push_back(
+          std::string("PERFETTO_CONSUMER_SOCK_NAME=") +
+          TestHelper::GetConsumerSocketName());
+      cmd.push_back(base::GetCurExecutableDir() + "/" + argv0);
+      cmd.insert(cmd.end(), args.begin(), args.end());
+    }
+
+    if (access(cmd[0].c_str(), F_OK)) {
+      PERFETTO_FATAL(
+          "Cannot find %s. Make sure that the target has been built and, on "
+          "Android, pushed to the device.",
+          cmd[0].c_str());
+    }
+
+    // This pipe blocks the execution of the child process until the main test
+    // process calls Run(). There are two conflicting problems here:
+    // 1) We can't fork() subprocesses too late, because the test spawns threads
+    //    for hosting the service. fork+threads = bad (see aosp/1089744).
+    // 2) We can't run the subprocess too early, because we need to wait that
+    //    the service threads are ready before trying to connect from the child
+    //    process.
+    sync_pipe_ = base::Pipe::Create();
+    int sync_pipe_rd = *sync_pipe_.rd;
+    subprocess_.args.preserve_fds.push_back(sync_pipe_rd);
+
+    // This lambda will be called on the forked child process after having
+    // setup pipe redirection and closed all FDs, right before the exec().
+    // The Subprocesss harness will take care of closing also |sync_pipe_.wr|.
+    subprocess_.args.entrypoint_for_testing = [sync_pipe_rd] {
+      // Don't add any logging here, all file descriptors are closed and trying
+      // to log will likely cause undefined behaviors.
+      char ignored = 0;
+      PERFETTO_CHECK(PERFETTO_EINTR(read(sync_pipe_rd, &ignored, 1)) > 0);
+      PERFETTO_CHECK(close(sync_pipe_rd) == 0 || errno == EINTR);
+    };
+
+    subprocess_.Start();
+    sync_pipe_.rd.reset();
   }
 
   friend class PerfettoCmdlineTest;
-
-  pid_t pid_;
-  base::Pipe err_pipe_;
-  base::Pipe start_pipe_;
+  base::Subprocess subprocess_;
+  base::Pipe sync_pipe_;
 };
 
 class PerfettoTest : public ::testing::Test {
@@ -268,14 +187,6 @@
     while (!ftrace_procfs_ && kTracingPaths[index]) {
       ftrace_procfs_ = FtraceProcfs::Create(kTracingPaths[index++]);
     }
-    if (!ftrace_procfs_)
-      return;
-    ftrace_procfs_->SetTracingOn(false);
-  }
-
-  void TearDown() override {
-    if (ftrace_procfs_)
-      ftrace_procfs_->SetTracingOn(false);
   }
 
   std::unique_ptr<FtraceProcfs> ftrace_procfs_;
@@ -312,7 +223,7 @@
     // You can not fork after you've started the service due to risk of
     // deadlocks.
     PERFETTO_CHECK(exec_allowed_);
-    return Exec::Create("perfetto", std::move(args), std::move(std_in));
+    return Exec("perfetto", std::move(args), std::move(std_in));
   }
 
   // Creates a process that represents the trigger_perfetto binary that will
@@ -323,7 +234,7 @@
     // You can not fork after you've started the service due to risk of
     // deadlocks.
     PERFETTO_CHECK(exec_allowed_);
-    return Exec::Create("trigger_perfetto", std::move(args), std::move(std_in));
+    return Exec("trigger_perfetto", std::move(args), std::move(std_in));
   }
 
   // Tests are allowed to freely use these variables.
@@ -346,13 +257,13 @@
 #define TEST_PRODUCER_SOCK_NAME ::perfetto::GetProducerSocket()
 #endif
 
-// TODO(b/73453011): reenable on more platforms (including standalone Android).
 #if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
 #define TreeHuggerOnly(x) x
 #else
 #define TreeHuggerOnly(x) DISABLED_##x
 #endif
 
+// TODO(b/73453011): reenable on more platforms (including standalone Android).
 TEST_F(PerfettoTest, TreeHuggerOnly(TestFtraceProducer)) {
   base::TestTaskRunner task_runner;
 
@@ -398,6 +309,7 @@
   }
 }
 
+// TODO(b/73453011): reenable on more platforms (including standalone Android).
 TEST_F(PerfettoTest, TreeHuggerOnly(TestFtraceFlush)) {
   base::TestTaskRunner task_runner;
 
@@ -414,7 +326,7 @@
 
   const uint32_t kTestTimeoutMs = 30000;
   TraceConfig trace_config;
-  trace_config.add_buffers()->set_size_kb(16);
+  trace_config.add_buffers()->set_size_kb(32);
   trace_config.set_duration_ms(kTestTimeoutMs);
 
   auto* ds_config = trace_config.add_data_sources()->mutable_config();
@@ -456,6 +368,7 @@
   ASSERT_EQ(marker_found, 1);
 }
 
+// TODO(b/73453011): reenable on more platforms (including standalone Android).
 TEST_F(PerfettoTest, TreeHuggerOnly(TestBatteryTracing)) {
   base::TestTaskRunner task_runner;
 
@@ -582,7 +495,7 @@
   helper.WaitForTracingDisabled();
 
   helper.ReadData();
-  helper.WaitForReadData();
+  helper.WaitForReadData(/* read_count */ 0, /* timeout_ms */ 10000);
 
   const auto& packets = helper.trace();
   ASSERT_EQ(packets.size(), kNumPackets);
@@ -685,6 +598,100 @@
   EXPECT_FALSE(helper.AttachConsumer("key"));
 }
 
+TEST_F(PerfettoTest, TestProducerProvidedSMB) {
+  base::TestTaskRunner task_runner;
+
+  TestHelper helper(&task_runner);
+  helper.CreateProducerProvidedSmb();
+
+  protos::gen::TestConfig test_config;
+  test_config.set_seed(42);
+  test_config.set_message_count(1);
+  test_config.set_message_size(1024);
+  test_config.set_send_batch_on_register(true);
+
+  // Write a first batch before connection.
+  helper.ProduceStartupEventBatch(test_config);
+
+  helper.StartServiceIfRequired();
+  helper.ConnectFakeProducer();
+  helper.ConnectConsumer();
+  helper.WaitForConsumerConnect();
+
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(1024);
+  trace_config.set_duration_ms(200);
+
+  auto* ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("android.perfetto.FakeProducer");
+  ds_config->set_target_buffer(0);
+  *ds_config->mutable_for_testing() = test_config;
+
+  // The data source is configured to emit another batch when it is started via
+  // send_batch_on_register in the TestConfig.
+  helper.StartTracing(trace_config);
+  helper.WaitForTracingDisabled();
+
+  EXPECT_TRUE(helper.IsShmemProvidedByProducer());
+
+  helper.ReadData();
+  helper.WaitForReadData();
+
+  const auto& packets = helper.trace();
+  // We should have produced two batches, one before the producer connected and
+  // another one when the data source was started.
+  ASSERT_EQ(packets.size(), 2u);
+  ASSERT_TRUE(packets[0].has_for_testing());
+  ASSERT_TRUE(packets[1].has_for_testing());
+}
+
+// Regression test for b/153142114.
+TEST_F(PerfettoTest, QueryServiceStateLargeResponse) {
+  base::TestTaskRunner task_runner;
+
+  TestHelper helper(&task_runner);
+  helper.StartServiceIfRequired();
+  helper.ConnectConsumer();
+  helper.WaitForConsumerConnect();
+
+  FakeProducer* producer = helper.ConnectFakeProducer();
+
+  // Register 5 data sources with very large descriptors. Each descriptor will
+  // max out the IPC message size, so that the service has no other choice
+  // than chunking them.
+  std::map<std::string, std::string> ds_expected;
+  for (int i = 0; i < 5; i++) {
+    DataSourceDescriptor dsd;
+    std::string name = "big_ds_" + std::to_string(i);
+    dsd.set_name(name);
+    std::string descriptor(ipc::kIPCBufferSize - 64, (' ' + i) % 64);
+    dsd.set_track_event_descriptor_raw(descriptor);
+    ds_expected[name] = std::move(descriptor);
+    producer->RegisterDataSource(dsd);
+  }
+
+  // Linearize the producer with the service. We need to make sure that all the
+  // RegisterDataSource() calls above have been seen by the service before
+  // continuing.
+  helper.SyncAndWaitProducer();
+
+  // Now invoke QueryServiceState() and wait for the reply. The service will
+  // send 6 (1 + 5) IPCs which will be merged together in
+  // producer_ipc_client_impl.cc.
+  auto svc_state = helper.QueryServiceStateAndWait();
+
+  ASSERT_GE(svc_state.producers().size(), 1u);
+
+  std::map<std::string, std::string> ds_found;
+  for (const auto& ds : svc_state.data_sources()) {
+    if (!base::StartsWith(ds.ds_descriptor().name(), "big_ds_"))
+      continue;
+    ds_found[ds.ds_descriptor().name()] =
+        ds.ds_descriptor().track_event_descriptor_raw();
+  }
+  EXPECT_THAT(ds_found, ElementsAreArray(ds_expected));
+}
+
 // Disable cmdline tests on sanitizets because they use fork() and that messes
 // up leak / races detections, which has been fixed only recently (see
 // https://github.com/google/sanitizers/issues/836 ).
@@ -836,11 +843,6 @@
   // time.
   trigger->set_stop_delay_ms(500);
 
-  // We have 6 normal preamble packets (start clock, trace config, clock,
-  // system info, sync marker, stats) and then since this is a trace with a
-  // trigger config we have an additional ReceivedTriggers packet.
-  constexpr size_t kPreamblePackets = 7;
-
   // We have to construct all the processes we want to fork before we start the
   // service with |StartServiceIfRequired()|. this is because it is unsafe
   // (could deadlock) to fork after we've spawned some threads which might
@@ -884,7 +886,7 @@
   base::ReadFile(path, &trace_str);
   protos::gen::Trace trace;
   ASSERT_TRUE(trace.ParseFromString(trace_str));
-  EXPECT_EQ(static_cast<int>(kPreamblePackets + kMessageCount),
+  EXPECT_EQ(static_cast<int>(kBuiltinPackets + kMessageCount),
             trace.packet_size());
   for (const auto& packet : trace.packet()) {
     if (packet.has_trace_config()) {
@@ -928,11 +930,6 @@
   trigger->set_name("trigger_name_3");
   trigger->set_stop_delay_ms(60000);
 
-  // We have 6 normal preamble packets (start clock, trace config, clock,
-  // system info, sync marker, stats) and then since this is a trace with a
-  // trigger config we have an additional ReceivedTriggers packet.
-  constexpr size_t kPreamblePackets = 8;
-
   // We have to construct all the processes we want to fork before we start the
   // service with |StartServiceIfRequired()|. this is because it is unsafe
   // (could deadlock) to fork after we've spawned some threads which might
@@ -977,7 +974,7 @@
   base::ReadFile(path, &trace_str);
   protos::gen::Trace trace;
   ASSERT_TRUE(trace.ParseFromString(trace_str));
-  EXPECT_EQ(static_cast<int>(kPreamblePackets + kMessageCount),
+  EXPECT_EQ(static_cast<int>(kBuiltinPackets + 1 + kMessageCount),
             trace.packet_size());
   bool seen_first_trigger = false;
   for (const auto& packet : trace.packet()) {
diff --git a/test/fake_producer.cc b/test/fake_producer.cc
index 730ec00..2fc18a3 100644
--- a/test/fake_producer.cc
+++ b/test/fake_producer.cc
@@ -22,6 +22,8 @@
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/utils.h"
 #include "perfetto/ext/traced/traced.h"
+#include "perfetto/ext/tracing/core/commit_data_request.h"
+#include "perfetto/ext/tracing/core/shared_memory_arbiter.h"
 #include "perfetto/ext/tracing/core/trace_packet.h"
 #include "perfetto/ext/tracing/core/trace_writer.h"
 #include "perfetto/tracing/core/data_source_config.h"
@@ -32,18 +34,29 @@
 
 namespace perfetto {
 
-FakeProducer::FakeProducer(const std::string& name) : name_(name) {}
+namespace {
+const MaybeUnboundBufferID kStartupTargetBufferReservationId = 1;
+}  // namespace
+
+FakeProducer::FakeProducer(const std::string& name,
+                           base::TaskRunner* task_runner)
+    : name_(name), task_runner_(task_runner) {}
 FakeProducer::~FakeProducer() = default;
 
-void FakeProducer::Connect(
-    const char* socket_name,
-    base::TaskRunner* task_runner,
-    std::function<void()> on_setup_data_source_instance,
-    std::function<void()> on_create_data_source_instance) {
+void FakeProducer::Connect(const char* socket_name,
+                           std::function<void()> on_connect,
+                           std::function<void()> on_setup_data_source_instance,
+                           std::function<void()> on_create_data_source_instance,
+                           std::unique_ptr<SharedMemory> shm,
+                           std::unique_ptr<SharedMemoryArbiter> shm_arbiter) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  task_runner_ = task_runner;
   endpoint_ = ProducerIPCClient::Connect(
-      socket_name, this, "android.perfetto.FakeProducer", task_runner);
+      socket_name, this, "android.perfetto.FakeProducer", task_runner_,
+      TracingService::ProducerSMBScrapingMode::kDefault,
+      /*shared_memory_size_hint_bytes=*/0,
+      /*shared_memory_page_size_hint_bytes=*/base::kPageSize, std::move(shm),
+      std::move(shm_arbiter));
+  on_connect_ = std::move(on_connect);
   on_setup_data_source_instance_ = std::move(on_setup_data_source_instance);
   on_create_data_source_instance_ = std::move(on_create_data_source_instance);
 }
@@ -53,6 +66,11 @@
   DataSourceDescriptor descriptor;
   descriptor.set_name(name_);
   endpoint_->RegisterDataSource(descriptor);
+  auto on_connect_callback = std::move(on_connect_);
+  auto task_runner = task_runner_;
+  endpoint_->Sync([task_runner, on_connect_callback] {
+    task_runner->PostTask(on_connect_callback);
+  });
 }
 
 void FakeProducer::OnDisconnect() {
@@ -68,13 +86,17 @@
 void FakeProducer::StartDataSource(DataSourceInstanceID,
                                    const DataSourceConfig& source_config) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  trace_writer_ = endpoint_->CreateTraceWriter(
-      static_cast<BufferID>(source_config.target_buffer()));
-  rnd_engine_ = std::minstd_rand0(source_config.for_testing().seed());
-  message_count_ = source_config.for_testing().message_count();
-  message_size_ = source_config.for_testing().message_size();
-  max_messages_per_second_ =
-      source_config.for_testing().max_messages_per_second();
+  if (trace_writer_) {
+    // Startup tracing was already active, just bind the target buffer.
+    endpoint_->MaybeSharedMemoryArbiter()->BindStartupTargetBuffer(
+        kStartupTargetBufferReservationId,
+        static_cast<BufferID>(source_config.target_buffer()));
+  } else {
+    // Common case: Start tracing now.
+    trace_writer_ = endpoint_->CreateTraceWriter(
+        static_cast<BufferID>(source_config.target_buffer()));
+    SetupFromConfig(source_config.for_testing());
+  }
   if (source_config.for_testing().send_batch_on_register()) {
     ProduceEventBatch(on_create_data_source_instance_);
   } else {
@@ -88,50 +110,45 @@
 }
 
 // Note: this can be called on a different thread.
-void FakeProducer::ProduceEventBatch(std::function<void()> callback) {
-  task_runner_->PostTask([this, callback] {
-    PERFETTO_CHECK(trace_writer_);
-    PERFETTO_CHECK(message_size_ > 1);
-    std::unique_ptr<char, base::FreeDeleter> payload(
-        static_cast<char*>(malloc(message_size_)));
-    memset(payload.get(), '.', message_size_);
-    payload.get()[message_size_ - 1] = 0;
+void FakeProducer::ProduceStartupEventBatch(
+    const protos::gen::TestConfig& config,
+    SharedMemoryArbiter* arbiter,
+    std::function<void()> callback) {
+  task_runner_->PostTask([this, config, arbiter, callback] {
+    SetupFromConfig(config);
 
-    base::TimeMillis start = base::GetWallTimeMs();
-    int64_t iterations = 0;
-    uint32_t messages_to_emit = message_count_;
-    while (messages_to_emit > 0) {
-      uint32_t messages_in_minibatch =
-          max_messages_per_second_ == 0
-              ? messages_to_emit
-              : std::min(max_messages_per_second_, messages_to_emit);
-      PERFETTO_DCHECK(messages_to_emit >= messages_in_minibatch);
+    PERFETTO_CHECK(!trace_writer_);
+    trace_writer_ =
+        arbiter->CreateStartupTraceWriter(kStartupTargetBufferReservationId);
 
-      for (uint32_t i = 0; i < messages_in_minibatch; i++) {
-        auto handle = trace_writer_->NewTracePacket();
-        handle->set_for_testing()->set_seq_value(
-            static_cast<uint32_t>(rnd_engine_()));
-        handle->set_for_testing()->set_str(payload.get(), message_size_);
-      }
-      messages_to_emit -= messages_in_minibatch;
-      iterations++;
+    EmitEventBatchOnTaskRunner({});
 
-      // Pause until the second boundary to make sure that we are adhering to
-      // the speed limitation.
-      if (max_messages_per_second_ > 0) {
-        int64_t expected_time_taken = iterations * 1000;
-        base::TimeMillis time_taken = base::GetWallTimeMs() - start;
-        while (time_taken.count() < expected_time_taken) {
-          usleep(static_cast<useconds_t>(
-              (expected_time_taken - time_taken.count()) * 1000));
-          time_taken = base::GetWallTimeMs() - start;
-        }
-      }
-      trace_writer_->Flush(messages_to_emit > 0 ? [] {} : callback);
-    }
+    // Issue callback right after writing - cannot wait for flush yet because
+    // we're not connected yet.
+    callback();
   });
 }
 
+// Note: this can be called on a different thread.
+void FakeProducer::ProduceEventBatch(std::function<void()> callback) {
+  task_runner_->PostTask(
+      [this, callback] { EmitEventBatchOnTaskRunner(callback); });
+}
+
+void FakeProducer::RegisterDataSource(const DataSourceDescriptor& desc) {
+  task_runner_->PostTask([this, desc] { endpoint_->RegisterDataSource(desc); });
+}
+
+void FakeProducer::CommitData(const CommitDataRequest& req,
+                              std::function<void()> callback) {
+  task_runner_->PostTask(
+      [this, req, callback] { endpoint_->CommitData(req, callback); });
+}
+
+void FakeProducer::Sync(std::function<void()> callback) {
+  task_runner_->PostTask([this, callback] { endpoint_->Sync(callback); });
+}
+
 void FakeProducer::OnTracingSetup() {}
 
 void FakeProducer::Flush(FlushRequestID flush_request_id,
@@ -143,4 +160,53 @@
   endpoint_->NotifyFlushComplete(flush_request_id);
 }
 
+void FakeProducer::SetupFromConfig(const protos::gen::TestConfig& config) {
+  rnd_engine_ = std::minstd_rand0(config.seed());
+  message_count_ = config.message_count();
+  message_size_ = config.message_size();
+  max_messages_per_second_ = config.max_messages_per_second();
+}
+
+void FakeProducer::EmitEventBatchOnTaskRunner(std::function<void()> callback) {
+  PERFETTO_CHECK(trace_writer_);
+  PERFETTO_CHECK(message_size_ > 1);
+  std::unique_ptr<char, base::FreeDeleter> payload(
+      static_cast<char*>(malloc(message_size_)));
+  memset(payload.get(), '.', message_size_);
+  payload.get()[message_size_ - 1] = 0;
+
+  base::TimeMillis start = base::GetWallTimeMs();
+  int64_t iterations = 0;
+  uint32_t messages_to_emit = message_count_;
+  while (messages_to_emit > 0) {
+    uint32_t messages_in_minibatch =
+        max_messages_per_second_ == 0
+            ? messages_to_emit
+            : std::min(max_messages_per_second_, messages_to_emit);
+    PERFETTO_DCHECK(messages_to_emit >= messages_in_minibatch);
+
+    for (uint32_t i = 0; i < messages_in_minibatch; i++) {
+      auto handle = trace_writer_->NewTracePacket();
+      handle->set_for_testing()->set_seq_value(
+          static_cast<uint32_t>(rnd_engine_()));
+      handle->set_for_testing()->set_str(payload.get(), message_size_);
+    }
+    messages_to_emit -= messages_in_minibatch;
+    iterations++;
+
+    // Pause until the second boundary to make sure that we are adhering to
+    // the speed limitation.
+    if (max_messages_per_second_ > 0) {
+      int64_t expected_time_taken = iterations * 1000;
+      base::TimeMillis time_taken = base::GetWallTimeMs() - start;
+      while (time_taken.count() < expected_time_taken) {
+        usleep(static_cast<useconds_t>(
+            (expected_time_taken - time_taken.count()) * 1000));
+        time_taken = base::GetWallTimeMs() - start;
+      }
+    }
+    trace_writer_->Flush(messages_to_emit > 0 ? [] {} : callback);
+  }
+}
+
 }  // namespace perfetto
diff --git a/test/fake_producer.h b/test/fake_producer.h
index 4f2c6be..c38e40f 100644
--- a/test/fake_producer.h
+++ b/test/fake_producer.h
@@ -30,20 +30,44 @@
 
 namespace perfetto {
 
+namespace protos {
+namespace gen {
+class TestConfig;
+}  // namespace gen
+}  // namespace protos
+
 class FakeProducer : public Producer {
  public:
-  explicit FakeProducer(const std::string& name);
+  explicit FakeProducer(const std::string& name, base::TaskRunner* task_runner);
   ~FakeProducer() override;
 
   void Connect(const char* socket_name,
-               base::TaskRunner* task_runner,
+               std::function<void()> on_connect,
                std::function<void()> on_setup_data_source_instance,
-               std::function<void()> on_create_data_source_instance);
+               std::function<void()> on_create_data_source_instance,
+               std::unique_ptr<SharedMemory> shm = nullptr,
+               std::unique_ptr<SharedMemoryArbiter> shm_arbiter = nullptr);
+
+  // Produces a batch of events (as configured by the passed config) before the
+  // producer is connected to the service using the provided unbound arbiter.
+  // Posts |callback| once the data was written. May only be called once.
+  void ProduceStartupEventBatch(
+      const protos::gen::TestConfig& config,
+      SharedMemoryArbiter* arbiter,
+      std::function<void()> callback = [] {});
 
   // Produces a batch of events (as configured in the DataSourceConfig) and
   // posts a callback when the service acknowledges the commit.
   void ProduceEventBatch(std::function<void()> callback = [] {});
 
+  void RegisterDataSource(const DataSourceDescriptor&);
+  void CommitData(const CommitDataRequest&, std::function<void()> callback);
+  void Sync(std::function<void()> callback);
+
+  bool IsShmemProvidedByProducer() const {
+    return endpoint_->IsShmemProvidedByProducer();
+  }
+
   // Producer implementation.
   void OnConnect() override;
   void OnDisconnect() override;
@@ -58,15 +82,17 @@
                              size_t /*num_data_sources*/) override {}
 
  private:
-  void Shutdown();
+  void SetupFromConfig(const protos::gen::TestConfig& config);
+  void EmitEventBatchOnTaskRunner(std::function<void()> callback);
 
   base::ThreadChecker thread_checker_;
-  base::TaskRunner* task_runner_ = nullptr;
   std::string name_;
+  base::TaskRunner* task_runner_ = nullptr;
   std::minstd_rand0 rnd_engine_;
   uint32_t message_size_ = 0;
   uint32_t message_count_ = 0;
   uint32_t max_messages_per_second_ = 0;
+  std::function<void()> on_connect_;
   std::function<void()> on_setup_data_source_instance_;
   std::function<void()> on_create_data_source_instance_;
   std::unique_ptr<TracingService::ProducerEndpoint> endpoint_;
diff --git a/test/gtest_and_gmock.h b/test/gtest_and_gmock.h
index aa733e2..90a8c85 100644
--- a/test/gtest_and_gmock.h
+++ b/test/gtest_and_gmock.h
@@ -31,6 +31,7 @@
 #pragma GCC diagnostic ignored "-Wdeprecated"
 #pragma GCC diagnostic ignored "-Wmissing-noreturn"
 #pragma GCC diagnostic ignored "-Wsign-conversion"
+#pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif  // __GNUC__
 
 #if defined(__clang__)
diff --git a/test/gtest_logcat_printer.cc b/test/gtest_logcat_printer.cc
new file mode 100644
index 0000000..f88e01b
--- /dev/null
+++ b/test/gtest_logcat_printer.cc
@@ -0,0 +1,83 @@
+/*
+ * 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 file registers a GTest listener that logs test begin/end in logcat.
+// This is to avoid problems like b/149852934 where the test output cannot be
+// correlated with the prouction code's logcat logs because they use different
+// clock sources.
+
+#include <android/log.h>
+
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace test {
+
+namespace {
+
+#define PERFETTO_TEST_LOG(...) \
+  __android_log_print(ANDROID_LOG_DEBUG, "perfetto", ##__VA_ARGS__)
+
+class LogcatPrinter : public testing::EmptyTestEventListener {
+ public:
+  LogcatPrinter();
+  ~LogcatPrinter() override;
+
+  // testing::EmptyTestEventListener:
+  void OnTestCaseStart(const testing::TestCase& test_case) override;
+  void OnTestStart(const testing::TestInfo& test_info) override;
+  void OnTestEnd(const testing::TestInfo& test_info) override;
+  void OnTestCaseEnd(const testing::TestCase& test_case) override;
+};
+
+LogcatPrinter::LogcatPrinter() = default;
+LogcatPrinter::~LogcatPrinter() = default;
+
+void LogcatPrinter::OnTestCaseStart(const testing::TestCase& test_case) {
+  PERFETTO_TEST_LOG("Test case start: %s", test_case.name());
+}
+
+void LogcatPrinter::OnTestStart(const testing::TestInfo& test_info) {
+  PERFETTO_TEST_LOG("Test start: %s.%s", test_info.test_case_name(),
+                    test_info.name());
+}
+
+void LogcatPrinter::OnTestEnd(const testing::TestInfo& test_info) {
+  const auto* result = test_info.result();
+  const char* state = "N/A";
+  if (result)
+    state = result->Passed() ? "PASS" : "FAIL";
+  PERFETTO_TEST_LOG("Test end: %s.%s [%s]", test_info.test_case_name(),
+                    test_info.name(), state);
+}
+
+void LogcatPrinter::OnTestCaseEnd(const testing::TestCase& test_case) {
+  PERFETTO_TEST_LOG("Test case end: %s. succeeded=%d, failed=%d",
+                    test_case.name(), test_case.successful_test_count(),
+                    test_case.failed_test_count());
+}
+
+// This static initializer
+void __attribute__((constructor)) __attribute__((visibility("default")))
+SetupGtestLogcatPrinter() {
+  static LogcatPrinter* instance = new LogcatPrinter();
+  auto& listeners = testing::UnitTest::GetInstance()->listeners();
+  listeners.Append(instance);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace perfetto
diff --git a/test/metrics/android_ion.out b/test/metrics/android_ion.out
index 48b7f8a..c7094fb 100644
--- a/test/metrics/android_ion.out
+++ b/test/metrics/android_ion.out
@@ -3,12 +3,14 @@
     name: "adsp"
     avg_size_bytes: 1000.0
     min_size_bytes: 1000.0
-    max_size_bytes: 1000.0
+    max_size_bytes: 1100.0
+    total_alloc_size_bytes: 1100.0
   }
   buffer {
     name: "system"
     avg_size_bytes: 1497.48743719
     min_size_bytes: 1000.0
     max_size_bytes: 2000.0
+    total_alloc_size_bytes: 2000.0
   }
 }
diff --git a/test/metrics/android_ion.py b/test/metrics/android_ion.py
index 4993245..6bff44f 100644
--- a/test/metrics/android_ion.py
+++ b/test/metrics/android_ion.py
@@ -25,9 +25,9 @@
 trace.add_process(3, 1, 'com.google.android.calendar')
 
 trace.add_ftrace_packet(cpu=0)
-trace.add_ion_event(ts=100, tid=3, heap_name='system', size=1000)
-trace.add_ion_event(ts=150, tid=3, heap_name='adsp', size=1000)
-trace.add_ion_event(ts=200, tid=3, heap_name='system', size=2000)
-trace.add_ion_event(ts=299, tid=3, heap_name='adsp', size=1000)
+trace.add_ion_event(ts=100, tid=3, heap_name='system', len=1000)
+trace.add_ion_event(ts=150, tid=3, heap_name='adsp', len=1000)
+trace.add_ion_event(ts=200, tid=3, heap_name='system', size=1000, len=1000)
+trace.add_ion_event(ts=299, tid=3, heap_name='adsp', size=1000, len=100)
 
 print(trace.trace.SerializeToString())
diff --git a/test/metrics/android_lmk_oom.out b/test/metrics/android_lmk_oom.out
new file mode 100644
index 0000000..31f0df5
--- /dev/null
+++ b/test/metrics/android_lmk_oom.out
@@ -0,0 +1,4 @@
+android_lmk {
+  total_count: 0
+  oom_victim_count: 1
+}
\ No newline at end of file
diff --git a/test/metrics/android_lmk_reason.py b/test/metrics/android_lmk_reason.py
index 78300c3..4756cf1 100644
--- a/test/metrics/android_lmk_reason.py
+++ b/test/metrics/android_lmk_reason.py
@@ -45,7 +45,7 @@
 trace.add_kernel_lmk(ts=202, tid=4)
 
 trace.add_ftrace_packet(cpu=0)
-trace.add_ion_event(ts=301, tid=5, heap_name='system', size=1000)
+trace.add_ion_event(ts=301, tid=5, heap_name='system', len=1000)
 trace.add_oom_score_update(ts=302, oom_score_adj=100, pid=5)
 trace.add_kernel_lmk(ts=303, tid=5)
 
diff --git a/test/metrics/android_mem_lmk.out b/test/metrics/android_mem_lmk.out
index 5a14e3d..762bdaf 100644
--- a/test/metrics/android_mem_lmk.out
+++ b/test/metrics/android_mem_lmk.out
@@ -4,4 +4,5 @@
     oom_score_adj: 900
     count: 1
   }
+  oom_victim_count: 0
 }
diff --git a/test/metrics/android_startup.out b/test/metrics/android_startup.out
index 5e60861..ea6ce94 100644
--- a/test/metrics/android_startup.out
+++ b/test/metrics/android_startup.out
@@ -3,6 +3,20 @@
     startup_id: 2
     package_name: "com.google.android.calendar"
     process_name: "com.google.android.calendar"
+    process: {
+      name: "com.google.android.calendar"
+      uid: 10001
+      package {
+        package_name: "com.google.android.calendar"
+        apk_version_code: 123
+        debuggable: false
+      }
+      packages_for_uid {
+        package_name: "com.google.android.calendar"
+        apk_version_code: 123
+        debuggable: false
+      }
+    }
     zygote_new_process: false
     activity_hosting_process_count: 2
     to_first_frame {
@@ -16,8 +30,9 @@
       other_processes_spawned_count: 1
       time_activity_manager {
         dur_ns: 8
+        dur_ms: 8e-06
       }
-      other_process_to_activity_cpu_ratio: 0.975609756098
+      dur_ms: 0.000108
     }
   }
 }
diff --git a/test/metrics/android_startup.py b/test/metrics/android_startup.py
index c3c180e..8a1ad9c 100644
--- a/test/metrics/android_startup.py
+++ b/test/metrics/android_startup.py
@@ -22,9 +22,12 @@
 trace.add_process_tree_packet()
 trace.add_process(1, 0, 'init')
 trace.add_process(2, 1, 'system_server')
-trace.add_process(3, 1, 'com.google.android.calendar')
+trace.add_process(3, 1, 'com.google.android.calendar', 10001)
 trace.add_process(4, 1, 'com.google.android.calendar')
 
+trace.add_package_list(
+    ts=1, name='com.google.android.calendar', uid=10001, version_code=123)
+
 trace.add_ftrace_packet(cpu=0)
 # Intent without any corresponding end state, will be ignored
 trace.add_atrace_begin(
diff --git a/test/metrics/android_startup_breakdown.out b/test/metrics/android_startup_breakdown.out
index 2dc19cd..e26b0b3 100644
--- a/test/metrics/android_startup_breakdown.out
+++ b/test/metrics/android_startup_breakdown.out
@@ -3,6 +3,9 @@
     startup_id: 1
     package_name: "com.google.android.calendar"
     process_name: "com.google.android.calendar"
+    process: {
+      name: "com.google.android.calendar"
+    }
     zygote_new_process: true
     to_first_frame {
       dur_ns: 108
@@ -15,15 +18,24 @@
       other_processes_spawned_count: 0
       time_activity_manager {
         dur_ns: 8
+        dur_ms: 8e-06
       }
       time_bind_application {
         dur_ns: 10
+        dur_ms: 1e-05
       }
       time_before_start_process {
         dur_ns: 18
+        dur_ms: 1.8e-05
       }
       time_during_start_process {
         dur_ns: 35
+        dur_ms: 3.5e-05
+      }
+      dur_ms: 0.000108
+      to_bind_application {
+        dur_ns: 83
+        dur_ms: 8.3e-05
       }
     }
     activity_hosting_process_count: 1
diff --git a/test/metrics/android_startup_cpu.out b/test/metrics/android_startup_cpu.out
index cda8111..9d3815a 100644
--- a/test/metrics/android_startup_cpu.out
+++ b/test/metrics/android_startup_cpu.out
@@ -1,13 +1,30 @@
 android_cpu {
   process_info {
     name: "Process1"
+    metrics {
+      mcycles: 2
+      runtime_ns: 3000000
+      min_freq_khz: 500000
+      max_freq_khz: 1400000
+      avg_freq_khz: 800000
+    }
+    core_type {
+      type: "little"
+      metrics {
+        mcycles: 2
+        runtime_ns: 3000000
+        min_freq_khz: 500000
+        max_freq_khz: 1400000
+        avg_freq_khz: 800000
+      }
+    }
     threads {
       name: "Process1"
       core {
         id: 0
         metrics {
           mcycles: 1
-          runtime_ns: 2
+          runtime_ns: 2000000
           min_freq_khz: 500000
           max_freq_khz: 500000
           avg_freq_khz: 500000
@@ -15,7 +32,7 @@
       }
       metrics {
         mcycles: 1
-        runtime_ns: 2
+        runtime_ns: 2000000
         min_freq_khz: 500000
         max_freq_khz: 500000
         avg_freq_khz: 500000
@@ -24,7 +41,7 @@
         type: "little"
         metrics {
           mcycles: 1
-          runtime_ns: 2
+          runtime_ns: 2000000
           min_freq_khz: 500000
           max_freq_khz: 500000
           avg_freq_khz: 500000
@@ -37,7 +54,7 @@
         id: 0
         metrics {
           mcycles: 1
-          runtime_ns: 1
+          runtime_ns: 1000000
           min_freq_khz: 1400000
           max_freq_khz: 1400000
           avg_freq_khz: 1400000
@@ -45,7 +62,7 @@
       }
       metrics {
         mcycles: 1
-        runtime_ns: 1
+        runtime_ns: 1000000
         min_freq_khz: 1400000
         max_freq_khz: 1400000
         avg_freq_khz: 1400000
@@ -54,30 +71,60 @@
         type: "little"
         metrics {
           mcycles: 1
-          runtime_ns: 1
+          runtime_ns: 1000000
           min_freq_khz: 1400000
           max_freq_khz: 1400000
           avg_freq_khz: 1400000
         }
       }
     }
-    metrics {
-      mcycles: 2
-      runtime_ns: 3
-      min_freq_khz: 500000
-      max_freq_khz: 1400000
-      avg_freq_khz: 800000
+    core {
+      id: 0
+      metrics {
+        mcycles: 2
+        runtime_ns: 3000000
+        min_freq_khz: 500000
+        max_freq_khz: 1400000
+        avg_freq_khz: 800000
+      }
     }
   }
   process_info {
     name: "Process2"
+    metrics {
+      mcycles: 21
+      runtime_ns: 7000000
+      min_freq_khz: 2000000
+      max_freq_khz: 8000000
+      avg_freq_khz: 3000000
+    }
+    core_type {
+      type: "big"
+      metrics {
+        mcycles: 16
+        runtime_ns: 5000000
+        min_freq_khz: 2000000
+        max_freq_khz: 8000000
+        avg_freq_khz: 3200000
+      }
+    }
+    core_type {
+      type: "little"
+      metrics {
+        mcycles: 5
+        runtime_ns: 2000000
+        min_freq_khz: 2500000
+        max_freq_khz: 2500000
+        avg_freq_khz: 2500000
+      }
+    }
     threads {
       name: "p2-t2"
       core {
         id: 6
         metrics {
           mcycles: 4
-          runtime_ns: 2
+          runtime_ns: 2000000
           min_freq_khz: 2000000
           max_freq_khz: 2000000
           avg_freq_khz: 2000000
@@ -85,7 +132,7 @@
       }
       metrics {
         mcycles: 4
-        runtime_ns: 2
+        runtime_ns: 2000000
         min_freq_khz: 2000000
         max_freq_khz: 2000000
         avg_freq_khz: 2000000
@@ -94,7 +141,7 @@
         type: "big"
         metrics {
           mcycles: 4
-          runtime_ns: 2
+          runtime_ns: 2000000
           min_freq_khz: 2000000
           max_freq_khz: 2000000
           avg_freq_khz: 2000000
@@ -107,7 +154,7 @@
         id: 6
         metrics {
           mcycles: 12
-          runtime_ns: 3
+          runtime_ns: 3000000
           min_freq_khz: 2000000
           max_freq_khz: 8000000
           avg_freq_khz: 4000000
@@ -115,7 +162,7 @@
       }
       metrics {
         mcycles: 12
-        runtime_ns: 3
+        runtime_ns: 3000000
         min_freq_khz: 2000000
         max_freq_khz: 8000000
         avg_freq_khz: 4000000
@@ -124,7 +171,7 @@
         type: "big"
         metrics {
           mcycles: 12
-          runtime_ns: 3
+          runtime_ns: 3000000
           min_freq_khz: 2000000
           max_freq_khz: 8000000
           avg_freq_khz: 4000000
@@ -137,7 +184,7 @@
         id: 0
         metrics {
           mcycles: 5
-          runtime_ns: 2
+          runtime_ns: 2000000
           min_freq_khz: 2500000
           max_freq_khz: 2500000
           avg_freq_khz: 2500000
@@ -145,7 +192,7 @@
       }
       metrics {
         mcycles: 5
-        runtime_ns: 2
+        runtime_ns: 2000000
         min_freq_khz: 2500000
         max_freq_khz: 2500000
         avg_freq_khz: 2500000
@@ -154,30 +201,70 @@
         type: "little"
         metrics {
           mcycles: 5
-          runtime_ns: 2
+          runtime_ns: 2000000
           min_freq_khz: 2500000
           max_freq_khz: 2500000
           avg_freq_khz: 2500000
         }
       }
     }
-    metrics {
-      mcycles: 21
-      runtime_ns: 7
-      min_freq_khz: 2000000
-      max_freq_khz: 8000000
-      avg_freq_khz: 3000000
+    core {
+      id: 0
+      metrics {
+        mcycles: 5
+        runtime_ns: 2000000
+        min_freq_khz: 2500000
+        max_freq_khz: 2500000
+        avg_freq_khz: 2500000
+      }
+    }
+    core {
+      id: 6
+      metrics {
+        mcycles: 16
+        runtime_ns: 5000000
+        min_freq_khz: 2000000
+        max_freq_khz: 8000000
+        avg_freq_khz: 3200000
+      }
     }
   }
   process_info {
     name: "Process3"
+    metrics {
+      mcycles: 18
+      runtime_ns: 6000000
+      min_freq_khz: 500000
+      max_freq_khz: 8000000
+      avg_freq_khz: 3150000
+    }
+    core_type {
+      type: "big"
+      metrics {
+        mcycles: 16
+        runtime_ns: 2000000
+        min_freq_khz: 8000000
+        max_freq_khz: 8000000
+        avg_freq_khz: 8000000
+      }
+    }
+    core_type {
+      type: "little"
+      metrics {
+        mcycles: 2
+        runtime_ns: 4000000
+        min_freq_khz: 500000
+        max_freq_khz: 1400000
+        avg_freq_khz: 725000
+      }
+    }
     threads {
       name: "Process3"
       core {
         id: 0
         metrics {
           mcycles: 2
-          runtime_ns: 4
+          runtime_ns: 4000000
           min_freq_khz: 500000
           max_freq_khz: 1400000
           avg_freq_khz: 725000
@@ -187,7 +274,7 @@
         id: 6
         metrics {
           mcycles: 16
-          runtime_ns: 2
+          runtime_ns: 2000000
           min_freq_khz: 8000000
           max_freq_khz: 8000000
           avg_freq_khz: 8000000
@@ -195,7 +282,7 @@
       }
       metrics {
         mcycles: 18
-        runtime_ns: 6
+        runtime_ns: 6000000
         min_freq_khz: 500000
         max_freq_khz: 8000000
         avg_freq_khz: 3150000
@@ -204,7 +291,7 @@
         type: "big"
         metrics {
           mcycles: 16
-          runtime_ns: 2
+          runtime_ns: 2000000
           min_freq_khz: 8000000
           max_freq_khz: 8000000
           avg_freq_khz: 8000000
@@ -214,19 +301,32 @@
         type: "little"
         metrics {
           mcycles: 2
-          runtime_ns: 4
+          runtime_ns: 4000000
           min_freq_khz: 500000
           max_freq_khz: 1400000
           avg_freq_khz: 725000
         }
       }
     }
-    metrics {
-      mcycles: 18
-      runtime_ns: 6
-      min_freq_khz: 500000
-      max_freq_khz: 8000000
-      avg_freq_khz: 3150000
+    core {
+      id: 0
+      metrics {
+        mcycles: 2
+        runtime_ns: 4000000
+        min_freq_khz: 500000
+        max_freq_khz: 1400000
+        avg_freq_khz: 725000
+      }
+    }
+    core {
+      id: 6
+      metrics {
+        mcycles: 16
+        runtime_ns: 2000000
+        min_freq_khz: 8000000
+        max_freq_khz: 8000000
+        avg_freq_khz: 8000000
+      }
     }
   }
-}
+}
\ No newline at end of file
diff --git a/test/metrics/android_startup_cpu.py b/test/metrics/android_startup_cpu.py
index 48d8b5a..35c51da 100644
--- a/test/metrics/android_startup_cpu.py
+++ b/test/metrics/android_startup_cpu.py
@@ -24,13 +24,13 @@
 trace.add_ftrace_packet(cpu=0)
 
 # CPU counters for CPU 0.
-trace.add_cpufreq(ts=9, freq=500000, cpu=0)
-trace.add_cpufreq(ts=15, freq=1400000, cpu=0)
-trace.add_cpufreq(ts=17, freq=2500000, cpu=0)
+trace.add_cpufreq(ts=9 * 1000000, freq=500000, cpu=0)
+trace.add_cpufreq(ts=15 * 1000000, freq=1400000, cpu=0)
+trace.add_cpufreq(ts=17 * 1000000, freq=2500000, cpu=0)
 
 # CPU counters for CPU 6.
-trace.add_cpufreq(ts=11, freq=2000000, cpu=6)
-trace.add_cpufreq(ts=15, freq=8000000, cpu=6)
+trace.add_cpufreq(ts=11 * 1000000, freq=2000000, cpu=6)
+trace.add_cpufreq(ts=15 * 1000000, freq=8000000, cpu=6)
 
 # Add 3 processes. This also adds one main thread per process.
 trace.add_process_tree_packet()
@@ -45,17 +45,17 @@
 
 # Schedule threads in CPU 0.
 trace.add_ftrace_packet(cpu=0)
-trace.add_sched(ts=10, prev_pid=0, next_pid=1)
-trace.add_sched(ts=12, prev_pid=1, next_pid=3)
-trace.add_sched(ts=16, prev_pid=3, next_pid=4)
-trace.add_sched(ts=17, prev_pid=4, next_pid=2)
-trace.add_sched(ts=19, prev_pid=2, next_pid=0)
+trace.add_sched(ts=10 * 1000000, prev_pid=0, next_pid=1)
+trace.add_sched(ts=12 * 1000000, prev_pid=1, next_pid=3)
+trace.add_sched(ts=16 * 1000000, prev_pid=3, next_pid=4)
+trace.add_sched(ts=17 * 1000000, prev_pid=4, next_pid=2)
+trace.add_sched(ts=19 * 1000000, prev_pid=2, next_pid=0)
 
 # Schedule threads in CPU 6.
 trace.add_ftrace_packet(cpu=6)
-trace.add_sched(ts=11, prev_pid=0, next_pid=5)
-trace.add_sched(ts=13, prev_pid=5, next_pid=6)
-trace.add_sched(ts=16, prev_pid=6, next_pid=3)
-trace.add_sched(ts=18, prev_pid=3, next_pid=0)
+trace.add_sched(ts=11 * 1000000, prev_pid=0, next_pid=5)
+trace.add_sched(ts=13 * 1000000, prev_pid=5, next_pid=6)
+trace.add_sched(ts=16 * 1000000, prev_pid=6, next_pid=3)
+trace.add_sched(ts=18 * 1000000, prev_pid=3, next_pid=0)
 
 print(trace.trace.SerializeToString())
diff --git a/test/metrics/android_startup_process_track.out b/test/metrics/android_startup_process_track.out
index 22f6066..3a3d5a8 100644
--- a/test/metrics/android_startup_process_track.out
+++ b/test/metrics/android_startup_process_track.out
@@ -3,6 +3,9 @@
     startup_id: 1
     package_name: "com.google.android.calendar"
     process_name: "com.google.android.calendar"
+    process: {
+      name: "com.google.android.calendar"
+    }
     zygote_new_process: false
     to_first_frame {
       dur_ns: 4
@@ -15,7 +18,9 @@
       other_processes_spawned_count: 0
       time_activity_manager {
         dur_ns: 2
+        dur_ms: 2e-06
       }
+      dur_ms: 4e-06
     }
     activity_hosting_process_count: 1
   }
@@ -23,6 +28,9 @@
     startup_id: 2
     package_name: "com.google.android.calendar"
     process_name: "com.google.android.calendar"
+    process: {
+      name: "com.google.android.calendar"
+    }
     zygote_new_process: false
     to_first_frame {
       dur_ns: 4
@@ -35,7 +43,9 @@
       other_processes_spawned_count: 0
       time_activity_manager {
         dur_ns: 2
+        dur_ms: 2e-06
       }
+      dur_ms: 4e-06
     }
     activity_hosting_process_count: 1
   }
diff --git a/test/metrics/android_task_names.out b/test/metrics/android_task_names.out
new file mode 100644
index 0000000..2a931e3
--- /dev/null
+++ b/test/metrics/android_task_names.out
@@ -0,0 +1,13 @@
+android_task_names {
+  process {
+    pid: 1
+    process_name: "init"
+    uid: 0
+  }
+  process {
+    pid: 2
+    process_name: "com.google.android.gm:process"
+    uid: 10001
+    uid_package_name: "com.google.android.gm"
+  }
+}
diff --git a/test/metrics/android_thread_time_in_state.out b/test/metrics/android_thread_time_in_state.out
new file mode 100644
index 0000000..f4d7af5
--- /dev/null
+++ b/test/metrics/android_thread_time_in_state.out
@@ -0,0 +1,32 @@
+android_thread_time_in_state {
+  processes {
+    metrics_by_core_type {
+      core_type: "unknown"
+      runtime_ms: 10
+    }
+    threads {
+      metrics_by_core_type {
+        core_type: "unknown"
+        runtime_ms: 10
+      }
+    }
+  }
+  processes {
+    metrics_by_core_type {
+      core_type: "unknown"
+      runtime_ms: 20
+    }
+    threads {
+      metrics_by_core_type {
+        core_type: "unknown"
+        runtime_ms: 10
+      }
+    }
+    threads {
+      metrics_by_core_type {
+        core_type: "unknown"
+        runtime_ms: 10
+      }
+    }
+  }
+}
diff --git a/test/metrics/index b/test/metrics/index
index 52edade..5483ba4 100644
--- a/test/metrics/index
+++ b/test/metrics/index
@@ -6,6 +6,8 @@
 android_mem_by_priority.py android_mem android_mem_by_priority.out
 android_lmk.py android_lmk android_mem_lmk.out
 android_lmk_reason.py android_lmk_reason android_lmk_reason.out
+../trace_processor/oom_kill.textproto android_lmk android_lmk_oom.out
+
 android_ion.py android_ion android_ion.out
 
 android_startup.py android_startup android_startup.out
@@ -22,7 +24,12 @@
 heap_profile_no_symbols.textproto unsymbolized_frames unsymbolized_frames.out
 
 ../trace_processor/heap_graph.textproto java_heap_stats java_heap_stats.out
+../trace_processor/heap_graph.textproto java_heap_histogram java_heap_histogram.out
 obfuscated_heap_graph.textproto unmapped_java_symbols unmapped_java_symbols.out
 
 # Json output
 ../data/memory_counters.pb trace_metadata trace_metadata.json.out
+
+process_uids.textproto android_task_names android_task_names.out
+
+../trace_processor/thread_time_in_state.textproto android_thread_time_in_state android_thread_time_in_state.out
diff --git a/test/metrics/java_heap_histogram.out b/test/metrics/java_heap_histogram.out
new file mode 100644
index 0000000..8558371
--- /dev/null
+++ b/test/metrics/java_heap_histogram.out
@@ -0,0 +1,37 @@
+java_heap_histogram {
+  instance_stats {
+    upid: 2
+    process {
+      name: "system_server"
+      uid: 1000
+    }
+    samples {
+      ts: 10
+      type_count {
+        type_name: "DeobfuscatedA"
+        obj_count: 1
+        reachable_obj_count: 0
+      }
+      type_count {
+        type_name: "DeobfuscatedA[]"
+        obj_count: 1
+        reachable_obj_count: 1
+      }
+      type_count {
+        type_name: "FactoryProducerDelegateImplActor"
+        obj_count: 1
+        reachable_obj_count: 1
+      }
+      type_count {
+        type_name: "Foo"
+        obj_count: 2
+        reachable_obj_count: 1
+      }
+      type_count {
+        type_name: "java.lang.Class<DeobfuscatedA[]>"
+        obj_count: 1
+        reachable_obj_count: 0
+      }
+    }
+  }
+}
diff --git a/test/metrics/java_heap_stats.out b/test/metrics/java_heap_stats.out
index 8ba3b96..48e2ed2 100644
--- a/test/metrics/java_heap_stats.out
+++ b/test/metrics/java_heap_stats.out
@@ -7,8 +7,11 @@
     }
     samples {
       ts: 10
-      heap_size: 480
-      reachable_heap_size: 96
+      heap_size: 992
+      reachable_heap_size: 352
+      obj_count: 6
+      reachable_obj_count: 3
+      anon_rss_and_swap_size: 4096000
     }
   }
 }
diff --git a/test/metrics/obfuscated_heap_graph.textproto b/test/metrics/obfuscated_heap_graph.textproto
index 4391067..4eb9ecf 100644
--- a/test/metrics/obfuscated_heap_graph.textproto
+++ b/test/metrics/obfuscated_heap_graph.textproto
@@ -29,12 +29,19 @@
       self_size: 64
       reference_field_id: 1
       reference_object_id: 0x02
+      reference_field_id: 2
+      reference_object_id: 0x01
     }
     objects {
       id: 0x02
       type_id: 2
       self_size: 32
     }
+    objects {
+      id: 0x03
+      type_id: 3
+      self_size: 32
+    }
     continued: true
     index: 1
   }
@@ -51,10 +58,18 @@
       iid: 2
       str: "a"
     }
+    type_names {
+      iid: 3
+      str: "java.lang.Class<abc[]>"
+    }
     field_names {
       iid: 1
       str: "FactoryProducerDelegateImplActor.a"
     }
+    field_names {
+      iid: 2
+      str: "FactoryProducerDelegateImplActor FactoryProducerDelegateImplActor.b"
+    }
     continued: false
     index: 2
   }
diff --git a/test/metrics/process_uids.textproto b/test/metrics/process_uids.textproto
new file mode 100644
index 0000000..bb6547c
--- /dev/null
+++ b/test/metrics/process_uids.textproto
@@ -0,0 +1,24 @@
+packet {
+  process_tree {
+    processes {
+      pid: 1
+      ppid: 0
+      cmdline: "init"
+      uid: 0
+    }
+    processes {
+      pid: 2
+      ppid: 1
+      cmdline: "com.google.android.gm:process"
+      uid: 10001
+    }
+  }
+}
+packet {
+  packages_list {
+    packages {
+      name: "com.google.android.gm"
+      uid: 10001
+    }
+  }
+}
diff --git a/test/metrics/unmapped_java_symbols.out b/test/metrics/unmapped_java_symbols.out
index b095d42..4628dd9 100644
--- a/test/metrics/unmapped_java_symbols.out
+++ b/test/metrics/unmapped_java_symbols.out
@@ -6,6 +6,13 @@
     }
     type_name: "FactoryProducerDelegateImplActor"
     type_name: "a"
-    field_name: "FactoryProducerDelegateImplActor.a"
+    type_name: "abc"
+    field {
+      field_name: "FactoryProducerDelegateImplActor.a"
+    }
+    field {
+      field_name: "FactoryProducerDelegateImplActor.b"
+      field_type_name: "FactoryProducerDelegateImplActor"
+    }
   }
 }
diff --git a/test/producer_socket_fuzzer.cc b/test/producer_socket_fuzzer.cc
index 46904df..5f7bc3a 100644
--- a/test/producer_socket_fuzzer.cc
+++ b/test/producer_socket_fuzzer.cc
@@ -37,8 +37,7 @@
 
   void OnConnect(base::UnixSocket* self, bool connected) override {
     PERFETTO_CHECK(connected && self->is_connected());
-    self->Send(data_, size_, self->fd(),
-               base::UnixSocket::BlockingMode::kBlocking);
+    self->Send(data_, size_, self->fd());
     data_sent_();
   }
 
diff --git a/test/synth_common.py b/test/synth_common.py
index 09bf07e..45cf760 100644
--- a/test/synth_common.py
+++ b/test/synth_common.py
@@ -59,10 +59,11 @@
     if curr is not None:
       rss_stat.curr = curr
 
-  def add_ion_event(self, ts, tid, heap_name, size):
+  def add_ion_event(self, ts, tid, heap_name, len, size=0):
     ftrace = self.__add_ftrace_event(ts, tid)
     ion = ftrace.ion_heap_grow
     ion.heap_name = heap_name
+    ion.len = len
     ion.total_allocated = size
 
   def add_oom_score_update(self, ts, oom_score_adj, pid):
@@ -344,6 +345,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/test_helper.cc b/test/test_helper.cc
index 51d9cb7..f397e8b 100644
--- a/test/test_helper.cc
+++ b/test/test_helper.cc
@@ -19,6 +19,7 @@
 #include "perfetto/ext/traced/traced.h"
 #include "perfetto/ext/tracing/core/trace_packet.h"
 #include "perfetto/ext/tracing/ipc/default_socket.h"
+#include "perfetto/tracing/core/tracing_service_state.h"
 
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 
@@ -42,6 +43,7 @@
       task_runner_(task_runner),
       service_thread_(TEST_PRODUCER_SOCK_NAME, TEST_CONSUMER_SOCK_NAME),
       fake_producer_thread_(TEST_PRODUCER_SOCK_NAME,
+                            WrapTask(CreateCheckpoint("producer.connect")),
                             WrapTask(CreateCheckpoint("producer.setup")),
                             WrapTask(CreateCheckpoint("producer.enabled"))) {}
 
@@ -64,7 +66,7 @@
         packet.ParseFromString(encoded_packet.GetRawBytesForTesting()));
     if (packet.has_clock_snapshot() || packet.has_trace_config() ||
         packet.has_trace_stats() || !packet.synchronization_marker().empty() ||
-        packet.has_system_info()) {
+        packet.has_system_info() || packet.has_service_event()) {
       continue;
     }
     PERFETTO_CHECK(packet.has_trusted_uid());
@@ -84,6 +86,9 @@
 
 FakeProducer* TestHelper::ConnectFakeProducer() {
   fake_producer_thread_.Connect();
+  // This will wait until the service has seen the RegisterDataSource() call
+  // (because of the Sync() in FakeProducer::OnConnect()).
+  RunUntilCheckpoint("producer.connect");
   return fake_producer_thread_.producer();
 }
 
@@ -114,6 +119,22 @@
   return success;
 }
 
+void TestHelper::CreateProducerProvidedSmb() {
+  fake_producer_thread_.CreateProducerProvidedSmb();
+}
+
+bool TestHelper::IsShmemProvidedByProducer() {
+  return fake_producer_thread_.producer()->IsShmemProvidedByProducer();
+}
+
+void TestHelper::ProduceStartupEventBatch(
+    const protos::gen::TestConfig& config) {
+  auto on_data_written = CreateCheckpoint("startup_data_written");
+  fake_producer_thread_.ProduceStartupEventBatch(config,
+                                                 WrapTask(on_data_written));
+  RunUntilCheckpoint("startup_data_written");
+}
+
 void TestHelper::StartTracing(const TraceConfig& config,
                               base::ScopedFile file) {
   trace_.clear();
@@ -160,6 +181,27 @@
                      timeout_ms);
 }
 
+void TestHelper::SyncAndWaitProducer() {
+  static int sync_id = 0;
+  std::string checkpoint_name = "producer_sync_" + std::to_string(++sync_id);
+  auto checkpoint = CreateCheckpoint(checkpoint_name);
+  fake_producer_thread_.producer()->Sync(
+      [this, &checkpoint] { task_runner_->PostTask(checkpoint); });
+  RunUntilCheckpoint(checkpoint_name);
+}
+
+TracingServiceState TestHelper::QueryServiceStateAndWait() {
+  TracingServiceState res;
+  auto checkpoint = CreateCheckpoint("query_svc_state");
+  auto callback = [&checkpoint, &res](bool, const TracingServiceState& tss) {
+    res = tss;
+    checkpoint();
+  };
+  endpoint_->QueryServiceState(callback);
+  RunUntilCheckpoint("query_svc_state");
+  return res;
+}
+
 std::function<void()> TestHelper::WrapTask(
     const std::function<void()>& function) {
   return [this, function] { task_runner_->PostTask(function); };
diff --git a/test/test_helper.h b/test/test_helper.h
index e18531e..0e1cc62 100644
--- a/test/test_helper.h
+++ b/test/test_helper.h
@@ -20,12 +20,14 @@
 #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/shared_memory_arbiter.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 "src/tracing/ipc/posix_shared_memory.h"
 #include "test/fake_producer.h"
 
 #include "protos/perfetto/trace/trace_packet.gen.h"
@@ -98,25 +100,29 @@
 class FakeProducerThread {
  public:
   FakeProducerThread(const std::string& producer_socket,
+                     std::function<void()> connect_callback,
                      std::function<void()> setup_callback,
-                     std::function<void()> connect_callback)
+                     std::function<void()> start_callback)
       : producer_socket_(producer_socket),
+        connect_callback_(std::move(connect_callback)),
         setup_callback_(std::move(setup_callback)),
-        connect_callback_(std::move(connect_callback)) {}
+        start_callback_(std::move(start_callback)) {
+    runner_ = base::ThreadTaskRunner::CreateAndStart("perfetto.prd.fake");
+    runner_->PostTaskAndWaitForTesting([this]() {
+      producer_.reset(
+          new FakeProducer("android.perfetto.FakeProducer", runner_->get()));
+    });
+  }
 
   ~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_));
+      producer_->Connect(producer_socket_.c_str(), std::move(connect_callback_),
+                         std::move(setup_callback_), std::move(start_callback_),
+                         std::move(shm_), std::move(shm_arbiter_));
     });
   }
 
@@ -124,13 +130,29 @@
 
   FakeProducer* producer() { return producer_.get(); }
 
+  void CreateProducerProvidedSmb() {
+    PosixSharedMemory::Factory factory;
+    shm_ = factory.CreateSharedMemory(1024 * 1024);
+    shm_arbiter_ =
+        SharedMemoryArbiter::CreateUnboundInstance(shm_.get(), base::kPageSize);
+  }
+
+  void ProduceStartupEventBatch(const protos::gen::TestConfig& config,
+                                std::function<void()> callback) {
+    PERFETTO_CHECK(shm_arbiter_);
+    producer_->ProduceStartupEventBatch(config, shm_arbiter_.get(), callback);
+  }
+
  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_;
+  std::function<void()> setup_callback_;
+  std::function<void()> start_callback_;
+  std::unique_ptr<SharedMemory> shm_;
+  std::unique_ptr<SharedMemoryArbiter> shm_arbiter_;
 };
 
 class TestHelper : public Consumer {
@@ -151,7 +173,11 @@
   void OnObservableEvents(const ObservableEvents&) override;
 
   void StartServiceIfRequired();
+
+  // Connects the producer and waits that the service has seen the
+  // RegisterDataSource() call.
   FakeProducer* ConnectFakeProducer();
+
   void ConnectConsumer();
   void StartTracing(const TraceConfig& config,
                     base::ScopedFile = base::ScopedFile());
@@ -160,12 +186,17 @@
   void ReadData(uint32_t read_count = 0);
   void DetachConsumer(const std::string& key);
   bool AttachConsumer(const std::string& key);
+  void CreateProducerProvidedSmb();
+  bool IsShmemProvidedByProducer();
+  void ProduceStartupEventBatch(const protos::gen::TestConfig& config);
 
   void WaitForConsumerConnect();
   void WaitForProducerSetup();
   void WaitForProducerEnabled();
   void WaitForTracingDisabled(uint32_t timeout_ms = 5000);
   void WaitForReadData(uint32_t read_count = 0, uint32_t timeout_ms = 5000);
+  void SyncAndWaitProducer();
+  TracingServiceState QueryServiceStateAndWait();
 
   std::string AddID(const std::string& checkpoint) {
     return checkpoint + "." + std::to_string(instance_num_);
diff --git a/test/trace_processor/android_sched_and_ps_end_reason_neq.out b/test/trace_processor/android_sched_and_ps_end_reason_neq.out
index 9b49703..1ab9944 100644
--- a/test/trace_processor/android_sched_and_ps_end_reason_neq.out
+++ b/test/trace_processor/android_sched_and_ps_end_reason_neq.out
@@ -1,6 +1,6 @@
 "end_state","count(*)"
+"DK",30
 "R",91197
+"R+",9428
 "S",110560
 "x",82
-"DK",30
-"R+",9428
diff --git a/test/trace_processor/counter_dur.sql b/test/trace_processor/counter_dur.sql
new file mode 100644
index 0000000..5bf2366
--- /dev/null
+++ b/test/trace_processor/counter_dur.sql
@@ -0,0 +1 @@
+select ts, dur from experimental_counter_dur where track_id in (1, 2, 3) order by dur limit 10;
diff --git a/test/trace_processor/counter_dur_example_android_trace_30s.out b/test/trace_processor/counter_dur_example_android_trace_30s.out
new file mode 100644
index 0000000..9208779
--- /dev/null
+++ b/test/trace_processor/counter_dur_example_android_trace_30s.out
@@ -0,0 +1,11 @@
+"ts","dur"
+100351738640,-1
+100351738640,-1
+100351738640,-1
+70731059648,19510835
+70731059648,19510835
+70731059648,19510835
+73727335051,23522762
+73727335051,23522762
+73727335051,23522762
+86726132752,24487554
diff --git a/test/trace_processor/end_reason_match.sql b/test/trace_processor/end_reason_match.sql
deleted file mode 100644
index 99ef8f8..0000000
--- a/test/trace_processor/end_reason_match.sql
+++ /dev/null
@@ -1,19 +0,0 @@
---
--- Copyright 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
---
---     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 end_state, count(*)
-from sched
-where end_state MATCH 'D'
-group by end_state
diff --git a/test/trace_processor/gpu_render_stages.out b/test/trace_processor/gpu_render_stages.out
index fc94c06..1485b99 100644
--- a/test/trace_processor/gpu_render_stages.out
+++ b/test/trace_processor/gpu_render_stages.out
@@ -1,26 +1,18 @@
-"track_name","track_desc","ts","dur","slice_name","depth","flat_key","string_value","context_id","render_target","render_pass","command_buffer","submission_id","hw_queue_id"
-"queue 1","queue desc 1",0,5,"render stage(1)",0,"[NULL]","[NULL]",0,0,0,0,0,1
-"queue 0","queue desc 0",0,5,"stage 0",0,"[NULL]","[NULL]",42,0,0,0,0,0
-"queue 1","queue desc 1",10,5,"stage 1",0,"description","stage desc 1",42,0,0,0,0,1
-"queue 2","[NULL]",20,5,"stage 2",0,"[NULL]","[NULL]",42,0,0,0,0,2
-"queue 0","queue desc 0",30,5,"stage 3",0,"[NULL]","[NULL]",42,0,0,0,0,0
-"Unknown GPU Queue 3","[NULL]",40,5,"render stage(4)",0,"[NULL]","[NULL]",42,0,0,0,0,3
-"queue 0","queue desc 0",50,5,"stage 0",0,"key1","value1",42,0,0,0,0,0
-"queue 0","queue desc 0",60,5,"stage 0",0,"key2","value2",42,0,0,0,0,0
-"queue 0","queue desc 0",60,5,"stage 0",0,"key1","value1",42,0,0,0,0,0
-"queue 0","queue desc 0",70,5,"stage 0",0,"key1","[NULL]",42,0,0,0,0,0
-"queue 0","queue desc 0",80,5,"stage 2",0,"[NULL]","[NULL]",42,0,0,0,0,0
-"queue 0","queue desc 0",90,5,"stage 0[0x10]",0,"VkCommandBuffer","0x0000000000000030",42,16,32,48,0,0
-"queue 0","queue desc 0",90,5,"stage 0[0x10]",0,"VkFramebuffer","0x0000000000000010",42,16,32,48,0,0
-"queue 0","queue desc 0",90,5,"stage 0[0x10]",0,"VkRenderPass","0x0000000000000020",42,16,32,48,0,0
-"queue 0","queue desc 0",100,5,"stage 0[0x10]",0,"VkCommandBuffer","0x0000000000000010 (command_buffer)",42,16,16,16,0,0
-"queue 0","queue desc 0",100,5,"stage 0[0x10]",0,"VkFramebuffer","0x0000000000000010",42,16,16,16,0,0
-"queue 0","queue desc 0",100,5,"stage 0[0x10]",0,"VkRenderPass","0x0000000000000010",42,16,16,16,0,0
-"queue 0","queue desc 0",110,5,"stage 0[0x10]",0,"VkCommandBuffer","0x0000000000000010 (command_buffer)",42,16,16,16,0,0
-"queue 0","queue desc 0",110,5,"stage 0[0x10]",0,"VkFramebuffer","0x0000000000000010",42,16,16,16,0,0
-"queue 0","queue desc 0",110,5,"stage 0[0x10]",0,"VkRenderPass","0x0000000000000010 (render_pass)",42,16,16,16,0,0
-"queue 0","queue desc 0",120,5,"stage 0[framebuffer]",0,"VkCommandBuffer","0x0000000000000010 (command_buffer)",42,16,16,16,0,0
-"queue 0","queue desc 0",120,5,"stage 0[framebuffer]",0,"VkFramebuffer","0x0000000000000010 (framebuffer)",42,16,16,16,0,0
-"queue 0","queue desc 0",120,5,"stage 0[framebuffer]",0,"VkRenderPass","0x0000000000000010 (render_pass)",42,16,16,16,0,0
-"queue 0","queue desc 0",130,5,"stage 0[renamed_buffer]",0,"VkFramebuffer","0x0000000000000010 (renamed_buffer)",42,16,0,0,0,0
-"Unknown GPU Queue ","[NULL]",140,5,"render stage(18446744073709551615)",0,"[NULL]","[NULL]",42,0,0,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,"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",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.sql b/test/trace_processor/gpu_render_stages.sql
index 1135262..c85d162 100644
--- a/test/trace_processor/gpu_render_stages.sql
+++ b/test/trace_processor/gpu_render_stages.sql
@@ -15,7 +15,8 @@
 --
 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, render_pass, command_buffer, 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
diff --git a/test/trace_processor/graphics_frame_events.out b/test/trace_processor/graphics_frame_events.out
index fd7c42a..7c1d441 100644
--- a/test/trace_processor/graphics_frame_events.out
+++ b/test/trace_processor/graphics_frame_events.out
@@ -1,20 +1,20 @@
-"ts","track_name","dur","slice_name","frame_id","key","layer_name"
-1,"layer1[buffer:1]",6,"Dequeue",11,"layer_name","layer1"
-2,"layer1[buffer:1]",7,"Queue",11,"layer_name","layer1"
-3,"layer1[buffer:1]",8,"Post",11,"layer_name","layer1"
-4,"layer1[buffer:1]",9,"AcquireFenceSignaled",11,"layer_name","layer1"
-5,"layer1[buffer:1]",10,"Latch",11,"layer_name","layer1"
-6,"layer1[buffer:1]",0,"PresentFenceSignaled",11,"layer_name","layer1"
-6,"layer2[buffer:2]",5,"Dequeue",12,"layer_name","layer2"
-6,"Displayed Frame",10,"1, 5",11,"layer_name","layer1, layer5"
-6,"layer5[buffer:5]",0,"PresentFenceSignaled",10,"layer_name","layer5"
-6,"layer1[buffer:2]",11,"Dequeue",12,"layer_name","layer1"
-7,"layer2[buffer:2]",0,"Queue",12,"layer_name","layer2"
-7,"layer2[buffer:2]",12,"Queue",12,"layer_name","layer2"
-7,"layer7[buffer:7]",12,"unknown_event",13,"layer_name","layer7"
-8,"layer2[buffer:2]",2,"Detach",14,"layer_name","layer2"
-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,"layer5[buffer:6]",0,"PresentFenceSignaled",17,"layer_name","layer5"
+"ts","track_name","dur","slice_name","frame_numbers","layer_names"
+1,"layer1[buffer:1]",6,"Dequeue","11","layer1"
+2,"layer1[buffer:1]",7,"Queue","11","layer1"
+3,"layer1[buffer:1]",8,"Post","11","layer1"
+4,"layer1[buffer:1]",9,"AcquireFenceSignaled","11","layer1"
+5,"layer1[buffer:1]",10,"Latch","11","layer1"
+6,"layer1[buffer:1]",0,"PresentFenceSignaled","11","layer1"
+6,"layer2[buffer:2]",5,"Dequeue","12","layer2"
+6,"Displayed Frame",10,"1, 5","11, 10","layer1, layer5"
+6,"layer5[buffer:5]",0,"PresentFenceSignaled","10","layer5"
+6,"layer1[buffer:2]",11,"Dequeue","12","layer1"
+7,"layer2[buffer:2]",0,"Queue","12","layer2"
+7,"layer2[buffer:2]",12,"Queue","12","layer2"
+7,"layer7[buffer:7]",12,"unknown_event","13","layer7"
+8,"layer2[buffer:2]",2,"Detach","14","layer2"
+8,"layer3[buffer:2]",13,"Post","12","layer3"
+9,"layer3[buffer:3]",5,"Attach","15","layer3"
+10,"layer3[buffer:3]",10,"Cancel","16","layer3"
+16,"Displayed Frame",-1,"6","[NULL]","[NULL]"
+16,"layer5[buffer:6]",0,"PresentFenceSignaled","17","layer5"
diff --git a/test/trace_processor/graphics_frame_events.sql b/test/trace_processor/graphics_frame_events.sql
index 1489944..dea74e8 100644
--- a/test/trace_processor/graphics_frame_events.sql
+++ b/test/trace_processor/graphics_frame_events.sql
@@ -13,11 +13,9 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 --
-select ts, track.name as track_name, dur, gpu_slice.name as slice_name,
-    frame_id, key, string_value as layer_name
+select ts, gpu_track.name as track_name, dur, frame_slice.name as slice_name,
+    frame_numbers, layer_names
 from gpu_track
-left join track using (id)
-left join gpu_slice on gpu_track.id=gpu_slice.track_id
-left join args on gpu_slice.arg_set_id=args.arg_set_id and args.key='layer_name'
+left join frame_slice on gpu_track.id=frame_slice.track_id
 where scope='graphics_frame_event'
 order by ts
diff --git a/test/trace_processor/heap_graph.textproto b/test/trace_processor/heap_graph.textproto
index 295e284..b0a304d 100644
--- a/test/trace_processor/heap_graph.textproto
+++ b/test/trace_processor/heap_graph.textproto
@@ -15,6 +15,28 @@
   }
 }
 packet {
+  timestamp: 1
+  process_stats {
+    processes {
+      pid: 2
+      rss_anon_kb: 1000
+      vm_swap_kb: 0
+      oom_score_adj: 0
+    }
+  }
+}
+packet {
+  timestamp: 10
+  process_stats {
+    processes {
+      pid: 2
+      rss_anon_kb: 1000
+      vm_swap_kb: 3000
+      oom_score_adj: 0
+    }
+  }
+}
+packet {
   trusted_packet_sequence_id: 999
   timestamp: 10
   heap_graph {
@@ -22,6 +44,7 @@
     roots {
       root_type: ROOT_JAVA_FRAME
       object_ids: 0x01
+      object_ids: 0x05
     }
     objects {
       id: 0x01
@@ -29,6 +52,8 @@
       self_size: 64
       reference_field_id: 1
       reference_object_id: 0x02
+      reference_field_id: 3
+      reference_object_id: 0x02
     }
     objects {
       id: 0x02
@@ -47,25 +72,53 @@
       reference_field_id: 2
       reference_object_id: 0x01
     }
+    objects {
+      id: 0x05
+      type_id: 4
+      self_size: 256
+    }
+    objects {
+      id: 0x06
+      type_id: 5
+      self_size: 256
+    }
     continued: true
-    index: 1
+    index: 0
   }
 }
 packet {
   trusted_packet_sequence_id: 999
+  timestamp: 10
   heap_graph {
     pid: 2
-    type_names {
+    location_names {
       iid: 1
-      str: "FactoryProducerDelegateImplActor"
+      str: "/data/app/ASDFG/invalid.test.android-SDASD/test.apk"
     }
-    type_names {
-      iid: 2
-      str: "Foo"
+    types {
+      id: 1
+      class_name: "FactoryProducerDelegateImplActor"
+      location_id: 1
     }
-    type_names {
-      iid: 3
-      str: "a"
+    types {
+      id: 2
+      class_name: "Foo"
+      location_id: 1
+    }
+    types {
+      id: 3
+      class_name: "a"
+      location_id: 1
+    }
+    types {
+      id: 4
+      class_name: "a[]"
+      location_id: 1
+    }
+    types {
+      id: 5
+      class_name: "java.lang.Class<a[]>"
+      location_id: 1
     }
     field_names {
       iid: 1
@@ -73,14 +126,19 @@
     }
     field_names {
       iid: 2
-      str: "a.a"
+      str: "int a.a"
+    }
+    field_names {
+      iid: 3
+      str: "a.b"
     }
     continued: false
-    index: 2
+    index: 1
   }
 }
 packet {
   deobfuscation_mapping {
+    package_name: "invalid.test.android"
     obfuscated_classes {
       obfuscated_name: "a"
       deobfuscated_name: "DeobfuscatedA"
@@ -88,6 +146,10 @@
         obfuscated_name: "a"
         deobfuscated_name: "deobfuscatedA"
       }
+      obfuscated_members {
+        obfuscated_name: "b"
+        deobfuscated_name: "Other.deobfuscatedA"
+      }
     }
   }
 }
diff --git a/test/trace_processor/heap_graph_baseapk.textproto b/test/trace_processor/heap_graph_baseapk.textproto
new file mode 100644
index 0000000..e0ea8e9
--- /dev/null
+++ b/test/trace_processor/heap_graph_baseapk.textproto
@@ -0,0 +1,155 @@
+packet {
+  process_tree {
+    processes {
+      pid: 1
+      ppid: 0
+      cmdline: "init"
+      uid: 0
+    }
+    processes {
+      pid: 2
+      ppid: 1
+      cmdline: "system_server"
+      uid: 1000
+    }
+  }
+}
+packet {
+  timestamp: 1
+  process_stats {
+    processes {
+      pid: 2
+      rss_anon_kb: 1000
+      vm_swap_kb: 0
+      oom_score_adj: 0
+    }
+  }
+}
+packet {
+  timestamp: 10
+  process_stats {
+    processes {
+      pid: 2
+      rss_anon_kb: 1000
+      vm_swap_kb: 3000
+      oom_score_adj: 0
+    }
+  }
+}
+packet {
+  trusted_packet_sequence_id: 999
+  timestamp: 10
+  heap_graph {
+    pid: 2
+    roots {
+      root_type: ROOT_JAVA_FRAME
+      object_ids: 0x01
+      object_ids: 0x05
+    }
+    objects {
+      id: 0x01
+      type_id: 1
+      self_size: 64
+      reference_field_id: 1
+      reference_object_id: 0x02
+      reference_field_id: 3
+      reference_object_id: 0x02
+    }
+    objects {
+      id: 0x02
+      type_id: 2
+      self_size: 32
+    }
+    objects {
+      id: 0x03
+      type_id: 2
+      self_size: 128
+    }
+    objects {
+      id: 0x04
+      type_id: 3
+      self_size: 256
+      reference_field_id: 2
+      reference_object_id: 0x01
+    }
+    objects {
+      id: 0x05
+      type_id: 4
+      self_size: 256
+    }
+    objects {
+      id: 0x06
+      type_id: 5
+      self_size: 256
+    }
+    continued: true
+    index: 0
+  }
+}
+packet {
+  trusted_packet_sequence_id: 999
+  timestamp: 10
+  heap_graph {
+    pid: 2
+    location_names {
+      iid: 1
+      str: "base.apk!classes.dex"
+    }
+    types {
+      id: 1
+      class_name: "FactoryProducerDelegateImplActor"
+      location_id: 1
+    }
+    types {
+      id: 2
+      class_name: "Foo"
+      location_id: 1
+    }
+    types {
+      id: 3
+      class_name: "a"
+      location_id: 1
+    }
+    types {
+      id: 4
+      class_name: "a[]"
+      location_id: 1
+    }
+    types {
+      id: 5
+      class_name: "java.lang.Class<a[]>"
+      location_id: 1
+    }
+    field_names {
+      iid: 1
+      str: "FactoryProducerDelegateImplActor.foo"
+    }
+    field_names {
+      iid: 2
+      str: "int a.a"
+    }
+    field_names {
+      iid: 3
+      str: "a.b"
+    }
+    continued: false
+    index: 1
+  }
+}
+packet {
+  deobfuscation_mapping {
+    package_name: "invalid.test.android"
+    obfuscated_classes {
+      obfuscated_name: "a"
+      deobfuscated_name: "DeobfuscatedA"
+      obfuscated_members {
+        obfuscated_name: "a"
+        deobfuscated_name: "deobfuscatedA"
+      }
+      obfuscated_members {
+        obfuscated_name: "b"
+        deobfuscated_name: "Other.deobfuscatedA"
+      }
+    }
+  }
+}
diff --git a/test/trace_processor/heap_graph_branching.textproto b/test/trace_processor/heap_graph_branching.textproto
new file mode 100644
index 0000000..d876fd0
--- /dev/null
+++ b/test/trace_processor/heap_graph_branching.textproto
@@ -0,0 +1,95 @@
+packet {
+  process_tree {
+    processes {
+      pid: 1
+      ppid: 0
+      cmdline: "init"
+      uid: 0
+    }
+    processes {
+      pid: 2
+      ppid: 1
+      cmdline: "system_server"
+      uid: 1000
+    }
+  }
+}
+packet {
+  trusted_packet_sequence_id: 999
+  timestamp: 10
+  heap_graph {
+    pid: 2
+    roots {
+      root_type: ROOT_JAVA_FRAME
+      object_ids: 0x01
+    }
+    objects {
+      id: 0x01
+      type_id: 1
+      self_size: 100
+      reference_field_id: 1
+      reference_object_id: 0x02
+      reference_field_id: 1
+      reference_object_id: 0x04
+    }
+    objects {
+      id: 0x02
+      type_id: 2
+      self_size: 200
+      reference_field_id: 1
+      reference_object_id: 0x03
+    }
+    objects {
+      id: 0x03
+      type_id: 3
+      self_size: 400
+    }
+    objects {
+      id: 0x04
+      type_id: 4
+      self_size: 800
+      reference_field_id: 1
+      reference_object_id: 0x05
+    }
+    objects {
+      id: 0x05
+      type_id: 5
+      self_size: 1600
+    }
+    continued: true
+    index: 0
+  }
+}
+packet {
+  trusted_packet_sequence_id: 999
+  heap_graph {
+    pid: 2
+    type_names {
+      iid: 1
+      str: "RootNode"
+    }
+    type_names {
+      iid: 2
+      str: "LeftChild0"
+    }
+    type_names {
+      iid: 3
+      str: "LeftChild1"
+    }
+    type_names {
+      iid: 4
+      str: "RightChild0"
+    }
+    type_names {
+      iid: 5
+      str: "RightChild1"
+    }
+    field_names {
+      iid: 1
+      str: "Node.next"
+    }
+    continued: false
+    index: 1
+  }
+}
+
diff --git a/test/trace_processor/heap_graph_flamegraph.out b/test/trace_processor/heap_graph_flamegraph.out
new file mode 100644
index 0000000..8ee3f57
--- /dev/null
+++ b/test/trace_processor/heap_graph_flamegraph.out
@@ -0,0 +1,4 @@
+"id","depth","name","map_name","count","cumulative_count","size","cumulative_size","parent_id"
+0,0,"FactoryProducerDelegateImplActor","JAVA",1,2,64,96,"[NULL]"
+1,1,"Foo","JAVA",1,1,32,32,0
+2,0,"DeobfuscatedA[]","JAVA",1,1,256,256,"[NULL]"
diff --git a/test/trace_processor/heap_graph_flamegraph.sql b/test/trace_processor/heap_graph_flamegraph.sql
index 157095f..92367f9 100644
--- a/test/trace_processor/heap_graph_flamegraph.sql
+++ b/test/trace_processor/heap_graph_flamegraph.sql
@@ -8,5 +8,8 @@
   size,
   cumulative_size,
   parent_id
-FROM experimental_flamegraph(601908408518618, 1, 'graph')
+FROM experimental_flamegraph(
+  (select max(graph_sample_ts) from heap_graph_object),
+  (select max(upid) from heap_graph_object),
+  'graph')
 LIMIT 10
diff --git a/test/trace_processor/heap_graph_flamegraph_focused.out b/test/trace_processor/heap_graph_flamegraph_focused.out
new file mode 100644
index 0000000..bb26353
--- /dev/null
+++ b/test/trace_processor/heap_graph_flamegraph_focused.out
@@ -0,0 +1,4 @@
+"id","depth","name","count","cumulative_count","size","cumulative_size","parent_id"
+0,0,"RootNode",1,3,100,700,"[NULL]"
+1,1,"LeftChild0",1,2,200,600,0
+2,2,"LeftChild1",1,1,400,400,1
diff --git a/test/trace_processor/heap_graph_flamegraph_focused.sql b/test/trace_processor/heap_graph_flamegraph_focused.sql
new file mode 100644
index 0000000..95d498a
--- /dev/null
+++ b/test/trace_processor/heap_graph_flamegraph_focused.sql
@@ -0,0 +1,15 @@
+SELECT
+  id,
+  depth,
+  name,
+  count,
+  cumulative_count,
+  size,
+  cumulative_size,
+  parent_id
+FROM experimental_flamegraph(
+  (select max(graph_sample_ts) from heap_graph_object),
+  (select max(upid) from heap_graph_object),
+  'graph')
+WHERE focus_str = 'left'
+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
index 102c246..8f822d1 100644
--- a/test/trace_processor/heap_graph_flamegraph_system-server-heap-graph.out
+++ b/test/trace_processor/heap_graph_flamegraph_system-server-heap-graph.out
@@ -1,11 +1,11 @@
 "id","depth","name","map_name","count","cumulative_count","size","cumulative_size","parent_id"
-0,4294967295,"java.lang.Class<boolean>","JAVA",2,4,240,276,"[NULL]"
-1,0,"java.lang.Object[]","JAVA",1,1,12,12,0
-2,0,"java.lang.String","JAVA",1,1,24,24,0
-3,4294967295,"java.lang.Class<byte>","JAVA",3,4,360,384,"[NULL]"
-4,0,"java.lang.String","JAVA",1,1,24,24,3
-5,4294967295,"java.lang.Class<short>","JAVA",2,3,240,264,"[NULL]"
-6,0,"java.lang.String","JAVA",1,1,24,24,5
-7,4294967295,"java.lang.Class<char>","JAVA",2,3,240,264,"[NULL]"
-8,0,"java.lang.String","JAVA",1,1,24,24,7
-9,4294967295,"java.lang.Class<int>","JAVA",2,3,240,264,"[NULL]"
+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_interleaved.textproto b/test/trace_processor/heap_graph_interleaved.textproto
index a27462c..fa736a4 100644
--- a/test/trace_processor/heap_graph_interleaved.textproto
+++ b/test/trace_processor/heap_graph_interleaved.textproto
@@ -65,29 +65,41 @@
       type_id: 1
       self_size: 64
     }
-    type_names {
+    location_names {
       iid: 1
-      str: "FactoryProducerDelegateImplActor"
+      str: "TEST:TEST"
+    }
+    types {
+      id: 1
+      class_name: "FactoryProducerDelegateImplActor"
+      location_id: 1
     }
     continued: false
-    index: 1
+    index: 0
   }
 }
 packet {
   trusted_packet_sequence_id: 999
   heap_graph {
     pid: 2
-    type_names {
+    location_names {
       iid: 1
-      str: "FactoryProducerDelegateImplActor"
+      str: "/data/app/ASDFG/invalid.test.android-SDASD/test.apk"
     }
-    type_names {
-      iid: 2
-      str: "Foo"
+    types {
+      id: 1
+      class_name: "FactoryProducerDelegateImplActor"
+      location_id: 1
     }
-    type_names {
-      iid: 3
-      str: "a"
+    types {
+      id: 2
+      class_name: "Foo"
+      location_id: 1
+    }
+    types {
+      id: 3
+      class_name: "a"
+      location_id: 1
     }
     field_names {
       iid: 1
@@ -98,11 +110,12 @@
       str: "a.a"
     }
     continued: false
-    index: 2
+    index: 1
   }
 }
 packet {
   deobfuscation_mapping {
+    package_name: "invalid.test.android"
     obfuscated_classes {
       obfuscated_name: "a"
       deobfuscated_name: "DeobfuscatedA"
diff --git a/test/trace_processor/heap_graph_interleaved_reference.out b/test/trace_processor/heap_graph_interleaved_reference.out
index 805e67c..a3eb377 100644
--- a/test/trace_processor/heap_graph_interleaved_reference.out
+++ b/test/trace_processor/heap_graph_interleaved_reference.out
@@ -1,3 +1,3 @@
-"id","type","reference_set_id","owner_id","owned_id","field_name","deobfuscated_field_name"
-0,"heap_graph_reference",0,1,2,"FactoryProducerDelegateImplActor.foo","[NULL]"
-1,"heap_graph_reference",1,4,1,"a.a","DeobfuscatedA.deobfuscatedA"
+"id","type","reference_set_id","owner_id","owned_id","field_name","field_type_name","deobfuscated_field_name"
+0,"heap_graph_reference",0,1,2,"FactoryProducerDelegateImplActor.foo","[NULL]","[NULL]"
+1,"heap_graph_reference",1,4,1,"a.a","[NULL]","DeobfuscatedA.deobfuscatedA"
diff --git a/test/trace_processor/heap_graph_legacy.textproto b/test/trace_processor/heap_graph_legacy.textproto
new file mode 100644
index 0000000..13fc018
--- /dev/null
+++ b/test/trace_processor/heap_graph_legacy.textproto
@@ -0,0 +1,146 @@
+packet {
+  process_tree {
+    processes {
+      pid: 1
+      ppid: 0
+      cmdline: "init"
+      uid: 0
+    }
+    processes {
+      pid: 2
+      ppid: 1
+      cmdline: "system_server"
+      uid: 1000
+    }
+  }
+}
+packet {
+  timestamp: 1
+  process_stats {
+    processes {
+      pid: 2
+      rss_anon_kb: 1000
+      vm_swap_kb: 0
+      oom_score_adj: 0
+    }
+  }
+}
+packet {
+  timestamp: 10
+  process_stats {
+    processes {
+      pid: 2
+      rss_anon_kb: 1000
+      vm_swap_kb: 3000
+      oom_score_adj: 0
+    }
+  }
+}
+packet {
+  trusted_packet_sequence_id: 999
+  timestamp: 10
+  heap_graph {
+    pid: 2
+    roots {
+      root_type: ROOT_JAVA_FRAME
+      object_ids: 0x01
+      object_ids: 0x05
+    }
+    objects {
+      id: 0x01
+      type_id: 1
+      self_size: 64
+      reference_field_id: 1
+      reference_object_id: 0x02
+      reference_field_id: 3
+      reference_object_id: 0x02
+    }
+    objects {
+      id: 0x02
+      type_id: 2
+      self_size: 32
+    }
+    objects {
+      id: 0x03
+      type_id: 2
+      self_size: 128
+    }
+    objects {
+      id: 0x04
+      type_id: 3
+      self_size: 256
+      reference_field_id: 2
+      reference_object_id: 0x01
+    }
+    objects {
+      id: 0x05
+      type_id: 4
+      self_size: 256
+    }
+    objects {
+      id: 0x06
+      type_id: 5
+      self_size: 256
+    }
+    continued: true
+    index: 0
+  }
+}
+packet {
+  trusted_packet_sequence_id: 999
+  timestamp: 10
+  heap_graph {
+    pid: 2
+    type_names {
+      iid: 1
+      str: "FactoryProducerDelegateImplActor"
+    }
+    type_names {
+      iid: 2
+      str: "Foo"
+    }
+    type_names {
+      iid: 3
+      str: "a"
+    }
+    type_names {
+      iid: 4
+      str: "a[]"
+    }
+    type_names {
+      iid: 5
+      str: "java.lang.Class<a[]>"
+    }
+    field_names {
+      iid: 1
+      str: "FactoryProducerDelegateImplActor.foo"
+    }
+    field_names {
+      iid: 2
+      str: "int a.a"
+    }
+    field_names {
+      iid: 3
+      str: "a.b"
+    }
+    continued: false
+    index: 1
+  }
+}
+packet {
+  deobfuscation_mapping {
+    package_name: "invalid.example.android"
+    obfuscated_classes {
+      obfuscated_name: "a"
+      deobfuscated_name: "DeobfuscatedA"
+      obfuscated_members {
+        obfuscated_name: "a"
+        deobfuscated_name: "deobfuscatedA"
+      }
+      obfuscated_members {
+        obfuscated_name: "b"
+        deobfuscated_name: "Other.deobfuscatedA"
+      }
+    }
+  }
+}
diff --git a/test/trace_processor/heap_graph_object.out b/test/trace_processor/heap_graph_object.out
index a25b67b..a5cf29e 100644
--- a/test/trace_processor/heap_graph_object.out
+++ b/test/trace_processor/heap_graph_object.out
@@ -1,5 +1,7 @@
 "id","type","upid","graph_sample_ts","object_id","self_size","retained_size","unique_retained_size","reference_set_id","reachable","type_name","deobfuscated_type_name","root_type"
 0,"heap_graph_object",2,10,1,64,-1,-1,0,1,"FactoryProducerDelegateImplActor","[NULL]","ROOT_JAVA_FRAME"
-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]"
+1,"heap_graph_object",2,10,2,32,-1,-1,2,1,"Foo","[NULL]","[NULL]"
+2,"heap_graph_object",2,10,3,128,-1,-1,2,0,"Foo","[NULL]","[NULL]"
+3,"heap_graph_object",2,10,4,256,-1,-1,2,0,"a","DeobfuscatedA","[NULL]"
+4,"heap_graph_object",2,10,5,256,-1,-1,3,1,"a[]","DeobfuscatedA[]","ROOT_JAVA_FRAME"
+5,"heap_graph_object",2,10,6,256,-1,-1,3,0,"java.lang.Class<a[]>","java.lang.Class<DeobfuscatedA[]>","[NULL]"
diff --git a/test/trace_processor/heap_graph_object.sql b/test/trace_processor/heap_graph_object.sql
index 234728f..d84ea66 100644
--- a/test/trace_processor/heap_graph_object.sql
+++ b/test/trace_processor/heap_graph_object.sql
@@ -13,4 +13,17 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 --
-select * from heap_graph_object
+select o.id,
+       o.type,
+       o.upid,
+       o.graph_sample_ts,
+       o.object_id,
+       o.self_size,
+       o.retained_size,
+       o.unique_retained_size,
+       o.reference_set_id,
+       o.reachable,
+       c.name as type_name,
+       c.deobfuscated_name as deobfuscated_type_name,
+       o.root_type
+from heap_graph_object o join heap_graph_class c on o.type_id = c.id
diff --git a/test/trace_processor/heap_graph_reference.out b/test/trace_processor/heap_graph_reference.out
index b4f14e9..a166b39 100644
--- a/test/trace_processor/heap_graph_reference.out
+++ b/test/trace_processor/heap_graph_reference.out
@@ -1,3 +1,4 @@
-"id","type","reference_set_id","owner_id","owned_id","field_name","deobfuscated_field_name"
-0,"heap_graph_reference",0,0,1,"FactoryProducerDelegateImplActor.foo","[NULL]"
-1,"heap_graph_reference",1,3,0,"a.a","DeobfuscatedA.deobfuscatedA"
+"id","type","reference_set_id","owner_id","owned_id","field_name","field_type_name","deobfuscated_field_name"
+0,"heap_graph_reference",0,0,1,"FactoryProducerDelegateImplActor.foo","[NULL]","[NULL]"
+1,"heap_graph_reference",0,0,1,"a.b","[NULL]","Other.deobfuscatedA"
+2,"heap_graph_reference",2,3,0,"a.a","int","DeobfuscatedA.deobfuscatedA"
diff --git a/test/trace_processor/heap_graph_two_locations.out b/test/trace_processor/heap_graph_two_locations.out
new file mode 100644
index 0000000..fdd18bf
--- /dev/null
+++ b/test/trace_processor/heap_graph_two_locations.out
@@ -0,0 +1,7 @@
+"id","type","upid","graph_sample_ts","object_id","self_size","retained_size","unique_retained_size","reference_set_id","reachable","type_name","deobfuscated_type_name","root_type"
+0,"heap_graph_object",2,10,1,64,-1,-1,0,1,"FactoryProducerDelegateImplActor","[NULL]","ROOT_JAVA_FRAME"
+1,"heap_graph_object",2,10,2,32,-1,-1,2,1,"a","Foo","[NULL]"
+2,"heap_graph_object",2,10,3,128,-1,-1,2,0,"a","Foo","[NULL]"
+3,"heap_graph_object",2,10,4,256,-1,-1,2,0,"a","DeobfuscatedA","[NULL]"
+4,"heap_graph_object",2,10,5,256,-1,-1,3,1,"a[]","DeobfuscatedA[]","ROOT_JAVA_FRAME"
+5,"heap_graph_object",2,10,6,256,-1,-1,3,0,"java.lang.Class<a[]>","java.lang.Class<DeobfuscatedA[]>","[NULL]"
diff --git a/test/trace_processor/heap_graph_two_locations.textproto b/test/trace_processor/heap_graph_two_locations.textproto
new file mode 100644
index 0000000..e70e525
--- /dev/null
+++ b/test/trace_processor/heap_graph_two_locations.textproto
@@ -0,0 +1,168 @@
+packet {
+  process_tree {
+    processes {
+      pid: 1
+      ppid: 0
+      cmdline: "init"
+      uid: 0
+    }
+    processes {
+      pid: 2
+      ppid: 1
+      cmdline: "system_server"
+      uid: 1000
+    }
+  }
+}
+packet {
+  timestamp: 1
+  process_stats {
+    processes {
+      pid: 2
+      rss_anon_kb: 1000
+      vm_swap_kb: 0
+      oom_score_adj: 0
+    }
+  }
+}
+packet {
+  timestamp: 10
+  process_stats {
+    processes {
+      pid: 2
+      rss_anon_kb: 1000
+      vm_swap_kb: 3000
+      oom_score_adj: 0
+    }
+  }
+}
+packet {
+  trusted_packet_sequence_id: 999
+  timestamp: 10
+  heap_graph {
+    pid: 2
+    roots {
+      root_type: ROOT_JAVA_FRAME
+      object_ids: 0x01
+      object_ids: 0x05
+    }
+    objects {
+      id: 0x01
+      type_id: 1
+      self_size: 64
+      reference_field_id: 1
+      reference_object_id: 0x02
+      reference_field_id: 3
+      reference_object_id: 0x02
+    }
+    objects {
+      id: 0x02
+      type_id: 2
+      self_size: 32
+    }
+    objects {
+      id: 0x03
+      type_id: 2
+      self_size: 128
+    }
+    objects {
+      id: 0x04
+      type_id: 3
+      self_size: 256
+      reference_field_id: 2
+      reference_object_id: 0x01
+    }
+    objects {
+      id: 0x05
+      type_id: 4
+      self_size: 256
+    }
+    objects {
+      id: 0x06
+      type_id: 5
+      self_size: 256
+    }
+    continued: true
+    index: 0
+  }
+}
+packet {
+  trusted_packet_sequence_id: 999
+  timestamp: 10
+  heap_graph {
+    pid: 2
+    location_names {
+      iid: 1
+      str: "/data/app/ASDFG/invalid.test.android-SDASD/test.apk"
+    }
+    location_names {
+      iid: 2
+      str: "/data/app/ASDFG/invalid.test2.android-SDASD/test.apk"
+    }
+    types {
+      id: 1
+      class_name: "FactoryProducerDelegateImplActor"
+      location_id: 1
+    }
+    types {
+      id: 2
+      class_name: "a"
+      location_id: 2
+    }
+    types {
+      id: 3
+      class_name: "a"
+      location_id: 1
+    }
+    types {
+      id: 4
+      class_name: "a[]"
+      location_id: 1
+    }
+    types {
+      id: 5
+      class_name: "java.lang.Class<a[]>"
+      location_id: 1
+    }
+    field_names {
+      iid: 1
+      str: "FactoryProducerDelegateImplActor.foo"
+    }
+    field_names {
+      iid: 2
+      str: "int a.a"
+    }
+    field_names {
+      iid: 3
+      str: "a.b"
+    }
+    continued: false
+    index: 1
+  }
+}
+packet {
+  deobfuscation_mapping {
+    package_name: "invalid.test.android"
+    obfuscated_classes {
+      obfuscated_name: "a"
+      deobfuscated_name: "DeobfuscatedA"
+      obfuscated_members {
+        obfuscated_name: "a"
+        deobfuscated_name: "deobfuscatedA"
+      }
+      obfuscated_members {
+        obfuscated_name: "b"
+        deobfuscated_name: "Other.deobfuscatedA"
+      }
+    }
+  }
+}
+packet {
+  deobfuscation_mapping {
+    package_name: "invalid.test2.android"
+    obfuscated_classes {
+      obfuscated_name: "a"
+      deobfuscated_name: "Foo"
+    }
+  }
+}
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 afd8064..255481b 100644
--- a/test/trace_processor/index
+++ b/test/trace_processor/index
@@ -19,6 +19,7 @@
 process_parent_pid_tracking_1.py process_parent_pid.sql process_parent_pid_process_parent_pid_tracking_1.out
 process_parent_pid_tracking_2.py process_parent_pid.sql process_parent_pid_process_parent_pid_tracking_2.out
 reused_thread_print.py process_tracking.sql process_tracking_reused_thread_print.out
+sde_tracing_mark_write.textproto slice_with_pid.sql slice_with_pid_sde_tracing_mark_write.out
 
 # Test for computing CPU time from sched events for threads.
 ../data/example_android_trace_30s.pb thread_cpu_time.sql thread_cpu_time_example_android_trace_30s.out
@@ -31,7 +32,6 @@
 # Sched reason
 ../data/android_sched_and_ps.pb end_reason_eq.sql android_sched_and_ps_end_reason_eq.out
 ../data/android_sched_and_ps.pb end_reason_neq.sql android_sched_and_ps_end_reason_neq.out
-../data/android_sched_and_ps.pb end_reason_match.sql android_sched_and_ps_end_reason_match.out
 
 # Sched wakeup
 ../data/android_sched_and_ps.pb sched_wakeup.sql sched_wakeup_android_sched_and_ps.out
@@ -54,12 +54,14 @@
 # LMK handling
 kernel_lmk.py lmk.sql lmk_kernel_lmk.out
 ../data/lmk_userspace.pb lmk.sql lmk_userspace_lmk.out
+oom_kill.textproto oom_kill.sql oom_kill.out
 
 # 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
+rss_stat_after_free.py rss_stat_after_free.sql rss_stat_after_free.out
 
 # Memory counters
 ../data/memory_counters.pb args_string_filter_null.sql memory_counters_args_string_filter_null.out
@@ -67,6 +69,7 @@
 ../data/memory_counters.pb args_string_is_not_null.sql memory_counters_args_string_is_not_null.out
 ../data/memory_counters.pb b120605557.sql memory_counters_b120605557.out
 ../data/memory_counters.pb global_memory_counter.sql global_memory_counter_memory_counters.out
+ion_stat.textproto ion_stat.sql ion_stat.out
 
 # Stats
 ../data/android_sched_and_ps.pb stats.sql android_sched_and_ps_stats.out
@@ -117,6 +120,7 @@
 counters_where_cpu.py counters_where_cpu.sql counters_where_cpu_counters_where_cpu.out
 counters_group_by_freq.py counters_group_by_freq.sql counters_group_by_freq_counters_group_by_freq.out
 ../data/example_android_trace_30s.pb filter_row_vector.sql filter_row_vector_example_android_trace_30s.out
+../data/example_android_trace_30s.pb counter_dur.sql counter_dur_example_android_trace_30s.out
 
 # Null printing
 synth_1.py nulls.sql nulls.out
@@ -131,6 +135,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
@@ -149,12 +154,30 @@
 
 heap_profile_jit.textproto heap_profile_frames.sql heap_profile_jit.out
 
+profiler_smaps.textproto profiler_smaps.sql profiler_smaps.out
+
+heap_graph_baseapk.textproto heap_graph_flamegraph.sql heap_graph_flamegraph.out
+heap_graph_baseapk.textproto heap_graph_object.sql heap_graph_object.out
+heap_graph_baseapk.textproto heap_graph_reference.sql heap_graph_reference.out
+
+heap_graph.textproto heap_graph_flamegraph.sql heap_graph_flamegraph.out
 heap_graph.textproto heap_graph_object.sql heap_graph_object.out
 heap_graph.textproto heap_graph_reference.sql heap_graph_reference.out
+heap_graph_two_locations.textproto heap_graph_object.sql heap_graph_two_locations.out
+# TODO(b/153552977): Stop supporting legacy heap graphs. These never made
+#                    it into a public release, so we should eventually stop
+#                    supporting workarounds for them.
+heap_graph_legacy.textproto heap_graph_flamegraph.sql heap_graph_flamegraph.out
+heap_graph_legacy.textproto heap_graph_object.sql heap_graph_object.out
+heap_graph_legacy.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
+heap_graph_branching.textproto heap_graph_flamegraph_focused.sql heap_graph_flamegraph_focused.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
@@ -162,12 +185,18 @@
 track_event_typed_args.textproto track_event_slices.sql track_event_typed_args_slices.out
 track_event_typed_args.textproto track_event_args.sql track_event_typed_args_args.out
 track_event_tracks.textproto track_event_slices.sql track_event_tracks_slices.out
+track_event_with_atrace.textproto track_event_slices.sql track_event_with_atrace.out
+track_event_merged_debug_annotations.textproto track_event_args.sql track_event_merged_debug_annotations_args.out
+track_event_counters.textproto track_event_slices.sql track_event_counters_slices.out
+track_event_counters.textproto track_event_counters.sql track_event_counters_counters.out
 
-# Parsing of an html file with systrace data inside
+# Parsing systrace files
 ../data/systrace.html systrace_html.sql systrace_html.out
+../data/trailing_empty.systrace sched_smoke.sql sched_smoke_trailing_empty.out
 
 # Config & metadata
 config_metadata.textproto metadata.sql config_metadata.out
+trigger_packet_trace.textproto triggers_packets.sql triggers_packets_trigger_packet_trace.out
 
 # Decoding of sched_waking events from a trace with compact scheduling events.
 # Verifies the contents of raw & instants tables.
@@ -179,3 +208,6 @@
 
 # Trace size
 ../data/android_sched_and_ps.pb trace_size.sql android_sched_and_ps_trace_size.out
+
+# Thread time_in_state
+thread_time_in_state.textproto thread_time_in_state.sql thread_time_in_state.out
diff --git a/test/trace_processor/ion_stat.out b/test/trace_processor/ion_stat.out
new file mode 100644
index 0000000..90c127e
--- /dev/null
+++ b/test/trace_processor/ion_stat.out
@@ -0,0 +1,3 @@
+"name","ts","value"
+"mem.ion",1234,200.000000
+"mem.ion_change",1234,100.000000
diff --git a/test/trace_processor/ion_stat.sql b/test/trace_processor/ion_stat.sql
new file mode 100644
index 0000000..1032ac6
--- /dev/null
+++ b/test/trace_processor/ion_stat.sql
@@ -0,0 +1,4 @@
+SELECT t.name, c.ts, c.value
+FROM counter c
+JOIN track t ON c.track_id = t.id
+WHERE t.name LIKE 'mem.ion%';
diff --git a/test/trace_processor/ion_stat.textproto b/test/trace_processor/ion_stat.textproto
new file mode 100644
index 0000000..b65f5b0
--- /dev/null
+++ b/test/trace_processor/ion_stat.textproto
@@ -0,0 +1,14 @@
+packet {
+  ftrace_events {
+    cpu: 4
+    event {
+      timestamp: 1234
+      pid: 4321
+      ion_stat {
+        buffer_id: 101010
+        len: 100
+        total_allocated: 200
+      }
+    }
+  }
+}
diff --git a/test/trace_processor/oom_kill.out b/test/trace_processor/oom_kill.out
new file mode 100644
index 0000000..7d54ced
--- /dev/null
+++ b/test/trace_processor/oom_kill.out
@@ -0,0 +1,2 @@
+"ts","name","pid","name"
+1234,"mem.oom_kill",1000,"com.google.android.gm"
diff --git a/test/trace_processor/oom_kill.sql b/test/trace_processor/oom_kill.sql
new file mode 100644
index 0000000..74b4f46
--- /dev/null
+++ b/test/trace_processor/oom_kill.sql
@@ -0,0 +1,3 @@
+SELECT ts, instant.name, process.pid, process.name
+FROM instant JOIN process ON instant.ref = process.upid
+WHERE instant.ref_type = 'upid';
diff --git a/test/trace_processor/oom_kill.textproto b/test/trace_processor/oom_kill.textproto
new file mode 100644
index 0000000..5beccd8
--- /dev/null
+++ b/test/trace_processor/oom_kill.textproto
@@ -0,0 +1,25 @@
+packet {
+  process_tree {
+    processes {
+      pid: 1000
+      ppid: 1
+      cmdline: "com.google.android.gm"
+    }
+    threads {
+      tid: 1001
+      tgid: 1000
+    }
+  }
+}
+packet {
+  ftrace_events {
+    cpu: 4
+    event {
+      timestamp: 1234
+      pid: 4321
+      mark_victim {
+        pid: 1001
+      }
+    }
+  }
+}
diff --git a/test/trace_processor/print_systrace_unsigned.out b/test/trace_processor/print_systrace_unsigned.out
index c12c888..e0acbcc 100644
--- a/test/trace_processor/print_systrace_unsigned.out
+++ b/test/trace_processor/print_systrace_unsigned.out
@@ -1,4 +1,4 @@
 "to_ftrace(id)"
 "       <unknown>-10    (   10) [000] .... 0.000000: kfree: call_site=16 ptr=32"
 "       <unknown>-10    (   10) [000] .... 0.000000: kfree: call_site=18446744073709551600 ptr=18446744073709551584"
-"       <unknown>-10    (   10) [000] .... 0.000000: kmalloc: bytes_alloc=32 bytes_req=16 call_site=18446744073709551600 gfp_flags=0 ptr=18446744073709551584"
+"       <unknown>-10    (   10) [000] .... 0.000000: kmalloc: gfp_flags=0 call_site=18446744073709551600 ptr=18446744073709551584 bytes_alloc=32 bytes_req=16"
diff --git a/test/trace_processor/profiler_smaps.out b/test/trace_processor/profiler_smaps.out
new file mode 100644
index 0000000..cd0111e
--- /dev/null
+++ b/test/trace_processor/profiler_smaps.out
@@ -0,0 +1,3 @@
+"id","type","upid","ts","path","size_kb","private_dirty_kb","swap_kb"
+0,"profiler_smaps",2,10,"/system/lib64/libc.so",20,4,4
+1,"profiler_smaps",2,10,"[anon: libc_malloc]",30,10,10
diff --git a/test/trace_processor/profiler_smaps.sql b/test/trace_processor/profiler_smaps.sql
new file mode 100644
index 0000000..53e5fd2
--- /dev/null
+++ b/test/trace_processor/profiler_smaps.sql
@@ -0,0 +1,2 @@
+select id, type, upid, ts, path, size_kb, private_dirty_kb, swap_kb
+from profiler_smaps;
diff --git a/test/trace_processor/profiler_smaps.textproto b/test/trace_processor/profiler_smaps.textproto
new file mode 100644
index 0000000..94d51a5
--- /dev/null
+++ b/test/trace_processor/profiler_smaps.textproto
@@ -0,0 +1,35 @@
+packet {
+  process_tree {
+    processes {
+      pid: 1
+      ppid: 0
+      cmdline: "init"
+      uid: 0
+    }
+    processes {
+      pid: 2
+      ppid: 1
+      cmdline: "system_server"
+      uid: 1000
+    }
+  }
+}
+packet {
+  trusted_packet_sequence_id: 999
+  timestamp: 10
+  smaps_packet {
+    pid: 2
+    entries {
+      path: "/system/lib64/libc.so"
+      size_kb: 20
+      private_dirty_kb: 4
+      swap_kb: 4
+    }
+    entries {
+      path: "[anon: libc_malloc]"
+      size_kb: 30
+      private_dirty_kb: 10
+      swap_kb: 10
+    }
+  }
+}
diff --git a/test/trace_processor/rss_stat_after_free.out b/test/trace_processor/rss_stat_after_free.out
new file mode 100644
index 0000000..d5c690e
--- /dev/null
+++ b/test/trace_processor/rss_stat_after_free.out
@@ -0,0 +1,3 @@
+"pid","last_rss","process_end"
+10,100,101
+11,90,"[NULL]"
diff --git a/test/trace_processor/rss_stat_after_free.py b/test/trace_processor/rss_stat_after_free.py
new file mode 100644
index 0000000..d7ac469
--- /dev/null
+++ b/test/trace_processor/rss_stat_after_free.py
@@ -0,0 +1,45 @@
+#!/usr/bin/python
+#!/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_process(11, 1, "other_process")
+
+trace.add_ftrace_packet(1)
+
+# Emit an event on an irrelevant thread.
+trace.add_rss_stat(90, tid=11, member=0, size=20, mm_id=0x5678, curr=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)
+
+# Emit an event on another thread which reuses the struct after free.
+trace.add_rss_stat(103, tid=11, member=0, size=10, mm_id=0x1234, curr=0)
+
+print(trace.trace.SerializeToString())
diff --git a/test/trace_processor/rss_stat_after_free.sql b/test/trace_processor/rss_stat_after_free.sql
new file mode 100644
index 0000000..d31fab7
--- /dev/null
+++ b/test/trace_processor/rss_stat_after_free.sql
@@ -0,0 +1,8 @@
+select
+  pid,
+  max(c.ts) as last_rss,
+  p.end_ts as process_end
+from counter c
+join process_counter_track t on c.track_id = t.id
+join process p using(upid)
+group by upid
\ No newline at end of file
diff --git a/test/trace_processor/rss_stat_mm_id copy.py b/test/trace_processor/rss_stat_mm_id copy.py
deleted file mode 100644
index d2b5e5a..0000000
--- a/test/trace_processor/rss_stat_mm_id copy.py
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/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/sched_smoke.sql b/test/trace_processor/sched_smoke.sql
new file mode 100644
index 0000000..14b26f5
--- /dev/null
+++ b/test/trace_processor/sched_smoke.sql
@@ -0,0 +1,2 @@
+SELECT COUNT(1)
+FROM sched
\ No newline at end of file
diff --git a/test/trace_processor/sched_smoke_trailing_empty.out b/test/trace_processor/sched_smoke_trailing_empty.out
new file mode 100644
index 0000000..7c28e5e
--- /dev/null
+++ b/test/trace_processor/sched_smoke_trailing_empty.out
@@ -0,0 +1,2 @@
+"COUNT(1)"
+2
diff --git a/test/trace_processor/sde_tracing_mark_write.textproto b/test/trace_processor/sde_tracing_mark_write.textproto
new file mode 100644
index 0000000..4a9728b
--- /dev/null
+++ b/test/trace_processor/sde_tracing_mark_write.textproto
@@ -0,0 +1,23 @@
+packet {
+  ftrace_events {
+    cpu: 0
+    event {
+      timestamp: 100
+      pid: 403
+      sde_tracing_mark_write {
+        pid: 403
+        trace_name: "test_event"
+        trace_begin: 1
+      }
+    }
+    event {
+      timestamp: 101
+      pid: 403
+      sde_tracing_mark_write {
+        pid: 403
+        trace_name: "test_event"
+        trace_begin: 0
+      }
+    }
+  }
+}
diff --git a/test/trace_processor/slice_with_pid.sql b/test/trace_processor/slice_with_pid.sql
new file mode 100644
index 0000000..c0ea0d2
--- /dev/null
+++ b/test/trace_processor/slice_with_pid.sql
@@ -0,0 +1,5 @@
+select s.name, dur, tid, pid
+from slice s
+join thread_track t on s.track_id = t.id
+join thread using(utid)
+left join process using(upid);
\ No newline at end of file
diff --git a/test/trace_processor/slice_with_pid_sde_tracing_mark_write.out b/test/trace_processor/slice_with_pid_sde_tracing_mark_write.out
new file mode 100644
index 0000000..ba562bf
--- /dev/null
+++ b/test/trace_processor/slice_with_pid_sde_tracing_mark_write.out
@@ -0,0 +1,2 @@
+"name","dur","tid","pid"
+"test_event",1,403,"[NULL]"
diff --git a/test/trace_processor/span_join_unpartitioned_empty.out b/test/trace_processor/span_join_unpartitioned_empty.out
index e64a7c8..882336b 100644
--- a/test/trace_processor/span_join_unpartitioned_empty.out
+++ b/test/trace_processor/span_join_unpartitioned_empty.out
@@ -2,5 +2,4 @@
 
 
 
-
 "ts","dur"
diff --git a/test/trace_processor/span_outer_join_empty_android_sched_and_ps.out b/test/trace_processor/span_outer_join_empty_android_sched_and_ps.out
index d2bd151..a64e00b 100644
--- a/test/trace_processor/span_outer_join_empty_android_sched_and_ps.out
+++ b/test/trace_processor/span_outer_join_empty_android_sched_and_ps.out
@@ -2,6 +2,5 @@
 
 
 
-
 "ts","dur","part"
 500,100,10
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/systrace_html.out b/test/trace_processor/systrace_html.out
index 9f6a165..9f64f70 100644
--- a/test/trace_processor/systrace_html.out
+++ b/test/trace_processor/systrace_html.out
@@ -1,3700 +1,3700 @@
 "ts","cpu","dur","ts_end","utid","end_state","priority","upid","name","tid"
-6824711826499000,6,20000,6824711826519000,2,"S",100,2,"kworker/u17:1",1134
-6824711826519000,6,69000,6824711826588000,3,"S",120,3,"kworker/6:1",14833
-6824711826574000,2,133000,6824711826707000,4,"S",120,5,"UsbFfs-worker",20308
-6824711826588000,6,32000,6824711826620000,6,"S",120,4,"rcu_preempt",7
-6824711826620000,6,72000,6824711826692000,9,"S",120,6,"rcuop/4",44
-6824711826692000,6,16000,6824711826708000,5,"S",120,7,"rcu_sched",8
-6824711826707000,2,81000,6824711826788000,8,"S",120,8,"rcuop/6",60
+6824711826499000,6,20000,6824711826519000,630,"S",100,630,"kworker/u17:1",1134
+6824711826519000,6,69000,6824711826588000,669,"S",120,669,"kworker/6:1",14833
+6824711826574000,2,133000,6824711826707000,2487,"S",120,739,"UsbFfs-worker",20308
+6824711826588000,6,32000,6824711826620000,5,"S",120,5,"rcu_preempt",7
+6824711826620000,6,72000,6824711826692000,38,"S",120,38,"rcuop/4",44
+6824711826692000,6,16000,6824711826708000,6,"S",120,6,"rcu_sched",8
+6824711826707000,2,81000,6824711826788000,52,"S",120,52,"rcuop/6",60
 6824711826708000,6,352000,6824711827060000,0,"R",120,"[NULL]","swapper/5",0
-6824711826730000,0,80000,6824711826810000,11,"S",120,9,"rcuop/0",10
-6824711826788000,2,81000,6824711826869000,10,"S",120,11,"rcuop/5",52
+6824711826730000,0,80000,6824711826810000,8,"S",120,8,"rcuop/0",10
+6824711826788000,2,81000,6824711826869000,45,"S",120,45,"rcuop/5",52
 6824711826810000,0,15054000,6824711841864000,0,"R",120,"[NULL]","swapper/5",0
-6824711826820000,3,33000,6824711826853000,14,"S",120,10,"rcuos/0",11
-6824711826853000,3,54000,6824711826907000,13,"S",120,12,"rcuop/1",20
-6824711826869000,2,165000,6824711827034000,7,"S",120,5,"adbd",20305
+6824711826820000,3,33000,6824711826853000,9,"S",120,9,"rcuos/0",11
+6824711826853000,3,54000,6824711826907000,17,"S",120,17,"rcuop/1",20
+6824711826869000,2,165000,6824711827034000,739,"S",120,739,"adbd",20305
 6824711826907000,3,380000,6824711827287000,0,"R",120,"[NULL]","swapper/5",0
-6824711826997000,1,21000,6824711827018000,15,"S",120,13,"rcuos/1",21
+6824711826997000,1,21000,6824711827018000,18,"S",120,18,"rcuos/1",21
 6824711827018000,1,27430000,6824711854448000,0,"R",120,"[NULL]","swapper/5",0
-6824711827034000,2,51000,6824711827085000,12,"S",120,15,"rcuop/7",68
-6824711827060000,6,8000,6824711827068000,16,"S",120,14,"kworker/u16:7",19422
+6824711827034000,2,51000,6824711827085000,59,"S",120,59,"rcuop/7",68
+6824711827060000,6,8000,6824711827068000,702,"S",120,702,"kworker/u16:7",19422
 6824711827068000,6,32000,6824711827100000,0,"R",120,"[NULL]","swapper/5",0
 6824711827085000,2,1808000,6824711828893000,0,"R",120,"[NULL]","swapper/5",0
-6824711827100000,6,4000,6824711827104000,16,"S",120,14,"kworker/u16:7",19422
+6824711827100000,6,4000,6824711827104000,702,"S",120,702,"kworker/u16:7",19422
 6824711827104000,6,9000,6824711827113000,0,"R",120,"[NULL]","swapper/5",0
-6824711827113000,6,31000,6824711827144000,16,"S",120,14,"kworker/u16:7",19422
-6824711827138000,7,5000,6824711827143000,17,"S",120,16,"kworker/u16:2",19725
+6824711827113000,6,31000,6824711827144000,702,"S",120,702,"kworker/u16:7",19422
+6824711827138000,7,5000,6824711827143000,711,"S",120,711,"kworker/u16:2",19725
 6824711827143000,7,13961000,6824711841104000,0,"R",120,"[NULL]","swapper/5",0
 6824711827144000,6,1680000,6824711828824000,0,"R",120,"[NULL]","swapper/5",0
 6824711827183000,4,899199000,6824712726382000,0,"R",120,"[NULL]","swapper/5",0
-6824711827287000,3,999000,6824711828286000,18,"x",120,17,"sh",20447
-6824711828286000,3,281000,6824711828567000,19,"x",120,5,"shell",20448
-6824711828567000,3,8000,6824711828575000,20,"S",120,18,"rcuos/2",29
-6824711828575000,3,250000,6824711828825000,7,"S",120,5,"adbd",20305
-6824711828824000,6,43000,6824711828867000,2,"S",100,2,"kworker/u17:1",1134
+6824711827287000,3,999000,6824711828286000,2731,"x",120,760,"sh",20447
+6824711828286000,3,281000,6824711828567000,2732,"x",120,739,"shell",20448
+6824711828567000,3,8000,6824711828575000,25,"S",120,25,"rcuos/2",29
+6824711828575000,3,250000,6824711828825000,739,"S",120,739,"adbd",20305
+6824711828824000,6,43000,6824711828867000,630,"S",100,630,"kworker/u17:1",1134
 6824711828825000,3,15722000,6824711844547000,0,"R",120,"[NULL]","swapper/5",0
-6824711828867000,6,19000,6824711828886000,3,"S",120,3,"kworker/6:1",14833
+6824711828867000,6,19000,6824711828886000,669,"S",120,669,"kworker/6:1",14833
 6824711828886000,6,64000,6824711828950000,0,"R",120,"[NULL]","swapper/5",0
-6824711828893000,2,28000,6824711828921000,4,"S",120,5,"UsbFfs-worker",20308
+6824711828893000,2,28000,6824711828921000,2487,"S",120,739,"UsbFfs-worker",20308
 6824711828921000,2,72000,6824711828993000,0,"R",120,"[NULL]","swapper/5",0
-6824711828950000,6,14000,6824711828964000,2,"S",100,2,"kworker/u17:1",1134
-6824711828964000,6,21000,6824711828985000,3,"S",120,3,"kworker/6:1",14833
+6824711828950000,6,14000,6824711828964000,630,"S",100,630,"kworker/u17:1",1134
+6824711828964000,6,21000,6824711828985000,669,"S",120,669,"kworker/6:1",14833
 6824711828985000,6,4000,6824711828989000,0,"R",120,"[NULL]","swapper/5",0
-6824711828989000,6,9000,6824711828998000,2,"S",100,2,"kworker/u17:1",1134
-6824711828993000,2,26000,6824711829019000,4,"S",120,5,"UsbFfs-worker",20308
+6824711828989000,6,9000,6824711828998000,630,"S",100,630,"kworker/u17:1",1134
+6824711828993000,2,26000,6824711829019000,2487,"S",120,739,"UsbFfs-worker",20308
 6824711828998000,6,102000,6824711829100000,0,"R",120,"[NULL]","swapper/5",0
 6824711829019000,2,208000,6824711829227000,0,"R",120,"[NULL]","swapper/5",0
-6824711829100000,6,34000,6824711829134000,2,"S",100,2,"kworker/u17:1",1134
+6824711829100000,6,34000,6824711829134000,630,"S",100,630,"kworker/u17:1",1134
 6824711829134000,6,38000,6824711829172000,0,"R",120,"[NULL]","swapper/5",0
-6824711829172000,6,16000,6824711829188000,2,"S",100,2,"kworker/u17:1",1134
-6824711829188000,6,23000,6824711829211000,3,"S",120,3,"kworker/6:1",14833
+6824711829172000,6,16000,6824711829188000,630,"S",100,630,"kworker/u17:1",1134
+6824711829188000,6,23000,6824711829211000,669,"S",120,669,"kworker/6:1",14833
 6824711829211000,6,11000,6824711829222000,0,"R",120,"[NULL]","swapper/5",0
-6824711829222000,6,33000,6824711829255000,2,"S",100,2,"kworker/u17:1",1134
-6824711829227000,2,70000,6824711829297000,4,"S",120,5,"UsbFfs-worker",20308
+6824711829222000,6,33000,6824711829255000,630,"S",100,630,"kworker/u17:1",1134
+6824711829227000,2,70000,6824711829297000,2487,"S",120,739,"UsbFfs-worker",20308
 6824711829255000,6,40000,6824711829295000,0,"R",120,"[NULL]","swapper/5",0
-6824711829295000,6,13000,6824711829308000,2,"S",100,2,"kworker/u17:1",1134
-6824711829297000,2,153000,6824711829450000,7,"S",120,5,"adbd",20305
-6824711829308000,6,19000,6824711829327000,3,"S",120,3,"kworker/6:1",14833
+6824711829295000,6,13000,6824711829308000,630,"S",100,630,"kworker/u17:1",1134
+6824711829297000,2,153000,6824711829450000,739,"S",120,739,"adbd",20305
+6824711829308000,6,19000,6824711829327000,669,"S",120,669,"kworker/6:1",14833
 6824711829327000,6,75000,6824711829402000,0,"R",120,"[NULL]","swapper/5",0
-6824711829402000,6,12000,6824711829414000,2,"S",100,2,"kworker/u17:1",1134
-6824711829414000,6,7000,6824711829421000,3,"S",120,3,"kworker/6:1",14833
+6824711829402000,6,12000,6824711829414000,630,"S",100,630,"kworker/u17:1",1134
+6824711829414000,6,7000,6824711829421000,669,"S",120,669,"kworker/6:1",14833
 6824711829421000,6,16000,6824711829437000,0,"R",120,"[NULL]","swapper/5",0
-6824711829437000,6,7000,6824711829444000,2,"S",100,2,"kworker/u17:1",1134
+6824711829437000,6,7000,6824711829444000,630,"S",100,630,"kworker/u17:1",1134
 6824711829444000,6,452000,6824711829896000,0,"R",120,"[NULL]","swapper/5",0
-6824711829450000,2,41000,6824711829491000,4,"S",120,5,"UsbFfs-worker",20308
-6824711829491000,2,68000,6824711829559000,7,"S",120,5,"adbd",20305
+6824711829450000,2,41000,6824711829491000,2487,"S",120,739,"UsbFfs-worker",20308
+6824711829491000,2,68000,6824711829559000,739,"S",120,739,"adbd",20305
 6824711829559000,2,4671000,6824711834230000,0,"R",120,"[NULL]","swapper/5",0
-6824711829896000,6,16000,6824711829912000,3,"S",120,3,"kworker/6:1",14833
+6824711829896000,6,16000,6824711829912000,669,"S",120,669,"kworker/6:1",14833
 6824711829912000,6,3511000,6824711833423000,0,"R",120,"[NULL]","swapper/5",0
-6824711833250000,5,36000,6824711833286000,22,"S",49,20,"sugov:4",606
-6824711833286000,5,153000,6824711833439000,21,"R+",120,19,"kswapd0",150
-6824711833423000,6,19000,6824711833442000,6,"S",120,4,"rcu_preempt",7
-6824711833439000,5,74000,6824711833513000,23,"S",120,21,"rcuop/2",28
-6824711833442000,6,8000,6824711833450000,5,"S",120,7,"rcu_sched",8
+6824711833250000,5,36000,6824711833286000,483,"S",49,483,"sugov:4",606
+6824711833286000,5,153000,6824711833439000,113,"R+",120,113,"kswapd0",150
+6824711833423000,6,19000,6824711833442000,5,"S",120,5,"rcu_preempt",7
+6824711833439000,5,74000,6824711833513000,24,"S",120,24,"rcuop/2",28
+6824711833442000,6,8000,6824711833450000,6,"S",120,6,"rcu_sched",8
 6824711833450000,6,7442000,6824711840892000,0,"R",120,"[NULL]","swapper/5",0
-6824711833513000,5,994000,6824711834507000,21,"S",120,19,"kswapd0",150
-6824711834230000,2,26000,6824711834256000,24,"S",120,22,"rcuop/3",36
+6824711833513000,5,994000,6824711834507000,113,"S",120,113,"kswapd0",150
+6824711834230000,2,26000,6824711834256000,31,"S",120,31,"rcuop/3",36
 6824711834256000,2,5778000,6824711840034000,0,"R",120,"[NULL]","swapper/5",0
 6824711834507000,5,6564000,6824711841071000,0,"R",120,"[NULL]","swapper/5",0
-6824711840034000,2,108000,6824711840142000,26,"S",49,23,"sugov:0",605
-6824711840142000,2,74000,6824711840216000,25,"S",120,24,"ksoftirqd/2",25
+6824711840034000,2,108000,6824711840142000,482,"S",49,482,"sugov:0",605
+6824711840142000,2,74000,6824711840216000,22,"S",120,22,"ksoftirqd/2",25
 6824711840216000,2,1649000,6824711841865000,0,"R",120,"[NULL]","swapper/5",0
-6824711840892000,6,39000,6824711840931000,6,"S",120,4,"rcu_preempt",7
-6824711840931000,6,24000,6824711840955000,5,"S",120,7,"rcu_sched",8
-6824711840955000,6,143000,6824711841098000,9,"S",120,6,"rcuop/4",44
-6824711841071000,5,11000,6824711841082000,27,"S",120,25,"rcuos/4",45
+6824711840892000,6,39000,6824711840931000,5,"S",120,5,"rcu_preempt",7
+6824711840931000,6,24000,6824711840955000,6,"S",120,6,"rcu_sched",8
+6824711840955000,6,143000,6824711841098000,38,"S",120,38,"rcuop/4",44
+6824711841071000,5,11000,6824711841082000,39,"S",120,39,"rcuos/4",45
 6824711841082000,5,5691000,6824711846773000,0,"R",120,"[NULL]","swapper/5",0
 6824711841098000,6,3082000,6824711844180000,0,"R",120,"[NULL]","swapper/5",0
-6824711841104000,7,11000,6824711841115000,28,"S",120,26,"rcuos/6",61
-6824711841115000,7,12000,6824711841127000,29,"S",120,27,"rcuos/7",69
-6824711841127000,7,10000,6824711841137000,30,"S",120,28,"kworker/7:2",14813
+6824711841104000,7,11000,6824711841115000,53,"S",120,53,"rcuos/6",61
+6824711841115000,7,12000,6824711841127000,60,"S",120,60,"rcuos/7",69
+6824711841127000,7,10000,6824711841137000,668,"S",120,668,"kworker/7:2",14813
 6824711841137000,7,290852000,6824712131989000,0,"R",120,"[NULL]","swapper/5",0
-6824711841864000,0,161000,6824711842025000,11,"S",120,9,"rcuop/0",10
-6824711841865000,2,1300000,6824711843165000,10,"S",120,11,"rcuop/5",52
+6824711841864000,0,161000,6824711842025000,8,"S",120,8,"rcuop/0",10
+6824711841865000,2,1300000,6824711843165000,45,"S",120,45,"rcuop/5",52
 6824711842025000,0,12144000,6824711854169000,0,"R",120,"[NULL]","swapper/5",0
-6824711843165000,2,225000,6824711843390000,8,"R",120,8,"rcuop/6",60
-6824711843390000,2,108000,6824711843498000,26,"S",49,23,"sugov:0",605
-6824711843498000,2,131000,6824711843629000,13,"S",120,12,"rcuop/1",20
-6824711843629000,2,152000,6824711843781000,8,"S",120,8,"rcuop/6",60
-6824711843781000,2,441000,6824711844222000,12,"S",120,15,"rcuop/7",68
-6824711844180000,6,95000,6824711844275000,16,"D",120,14,"kworker/u16:7",19422
-6824711844222000,2,48000,6824711844270000,31,"S",120,29,"kworker/2:0",18823
+6824711843165000,2,225000,6824711843390000,52,"R",120,52,"rcuop/6",60
+6824711843390000,2,108000,6824711843498000,482,"S",49,482,"sugov:0",605
+6824711843498000,2,131000,6824711843629000,17,"S",120,17,"rcuop/1",20
+6824711843629000,2,152000,6824711843781000,52,"S",120,52,"rcuop/6",60
+6824711843781000,2,441000,6824711844222000,59,"S",120,59,"rcuop/7",68
+6824711844180000,6,95000,6824711844275000,702,"D",120,702,"kworker/u16:7",19422
+6824711844222000,2,48000,6824711844270000,694,"S",120,694,"kworker/2:0",18823
 6824711844270000,2,2752000,6824711847022000,0,"R",120,"[NULL]","swapper/5",0
 6824711844275000,6,572000,6824711844847000,0,"R",120,"[NULL]","swapper/5",0
-6824711844547000,3,172000,6824711844719000,32,"S",120,30,"smem_native_rpm",87
+6824711844547000,3,172000,6824711844719000,77,"S",120,77,"smem_native_rpm",87
 6824711844719000,3,2190000,6824711846909000,0,"R",120,"[NULL]","swapper/5",0
-6824711844847000,6,13000,6824711844860000,16,"S",120,14,"kworker/u16:7",19422
+6824711844847000,6,13000,6824711844860000,702,"S",120,702,"kworker/u16:7",19422
 6824711844860000,6,1757000,6824711846617000,0,"R",120,"[NULL]","swapper/5",0
-6824711846617000,6,22000,6824711846639000,6,"S",120,4,"rcu_preempt",7
-6824711846639000,6,16000,6824711846655000,5,"S",120,7,"rcu_sched",8
-6824711846655000,6,11000,6824711846666000,3,"S",120,3,"kworker/6:1",14833
-6824711846666000,6,149000,6824711846815000,16,"S",120,14,"kworker/u16:7",19422
-6824711846773000,5,35000,6824711846808000,23,"S",120,21,"rcuop/2",28
+6824711846617000,6,22000,6824711846639000,5,"S",120,5,"rcu_preempt",7
+6824711846639000,6,16000,6824711846655000,6,"S",120,6,"rcu_sched",8
+6824711846655000,6,11000,6824711846666000,669,"S",120,669,"kworker/6:1",14833
+6824711846666000,6,149000,6824711846815000,702,"S",120,702,"kworker/u16:7",19422
+6824711846773000,5,35000,6824711846808000,24,"S",120,24,"rcuop/2",28
 6824711846808000,5,88282000,6824711935090000,0,"R",120,"[NULL]","swapper/5",0
 6824711846815000,6,7290000,6824711854105000,0,"R",120,"[NULL]","swapper/5",0
-6824711846909000,3,64000,6824711846973000,20,"S",120,18,"rcuos/2",29
-6824711846973000,3,63000,6824711847036000,33,"S",120,31,"rcuos/3",37
-6824711847022000,2,328000,6824711847350000,24,"S",120,22,"rcuop/3",36
+6824711846909000,3,64000,6824711846973000,25,"S",120,25,"rcuos/2",29
+6824711846973000,3,63000,6824711847036000,32,"S",120,32,"rcuos/3",37
+6824711847022000,2,328000,6824711847350000,31,"S",120,31,"rcuop/3",36
 6824711847036000,3,3496000,6824711850532000,0,"R",120,"[NULL]","swapper/5",0
 6824711847350000,2,8611000,6824711855961000,0,"R",120,"[NULL]","swapper/5",0
-6824711850532000,3,50000,6824711850582000,34,"S",120,32,"ksoftirqd/3",33
+6824711850532000,3,50000,6824711850582000,29,"S",120,29,"ksoftirqd/3",33
 6824711850582000,3,24018000,6824711874600000,0,"R",120,"[NULL]","swapper/5",0
-6824711854105000,6,13000,6824711854118000,6,"S",120,4,"rcu_preempt",7
-6824711854118000,6,7000,6824711854125000,5,"S",120,7,"rcu_sched",8
+6824711854105000,6,13000,6824711854118000,5,"S",120,5,"rcu_preempt",7
+6824711854118000,6,7000,6824711854125000,6,"S",120,6,"rcu_sched",8
 6824711854125000,6,6654000,6824711860779000,0,"R",120,"[NULL]","swapper/5",0
-6824711854169000,0,120000,6824711854289000,35,"S",120,33,"kworker/0:5",20371
+6824711854169000,0,120000,6824711854289000,743,"S",120,743,"kworker/0:5",20371
 6824711854289000,0,462000,6824711854751000,0,"R",120,"[NULL]","swapper/5",0
-6824711854448000,1,404000,6824711854852000,36,"S",111,34,"SDM_EventThread",685
-6824711854751000,0,327000,6824711855078000,38,"S",120,35,"HwBinder:640_1",721
+6824711854448000,1,404000,6824711854852000,786,"S",111,494,"SDM_EventThread",685
+6824711854751000,0,327000,6824711855078000,777,"S",120,493,"HwBinder:640_1",721
 6824711854852000,1,326000,6824711855178000,0,"R",120,"[NULL]","swapper/5",0
 6824711855078000,0,471000,6824711855549000,0,"R",120,"[NULL]","swapper/5",0
-6824711855178000,1,179000,6824711855357000,40,"S",97,35,"DispSync",676
+6824711855178000,1,179000,6824711855357000,771,"S",97,493,"DispSync",676
 6824711855357000,1,545000,6824711855902000,0,"R",120,"[NULL]","swapper/5",0
-6824711855549000,0,272000,6824711855821000,41,"S",97,35,"app",678
+6824711855549000,0,272000,6824711855821000,773,"S",97,493,"app",678
 6824711855821000,0,1794000,6824711857615000,0,"R",120,"[NULL]","swapper/5",0
-6824711855902000,1,1771000,6824711857673000,42,"S",120,36,"ndroid.systemui",1664
-6824711855961000,2,72000,6824711856033000,40,"S",97,35,"DispSync",676
+6824711855902000,1,1771000,6824711857673000,644,"S",120,644,"ndroid.systemui",1664
+6824711855961000,2,72000,6824711856033000,771,"S",97,493,"DispSync",676
 6824711856033000,2,5351000,6824711861384000,0,"R",120,"[NULL]","swapper/5",0
-6824711857615000,0,250000,6824711857865000,43,"S",120,35,"Binder:640_2",675
+6824711857615000,0,250000,6824711857865000,770,"S",120,493,"Binder:640_2",675
 6824711857673000,1,347000,6824711858020000,0,"R",120,"[NULL]","swapper/5",0
 6824711857865000,0,426000,6824711858291000,0,"R",120,"[NULL]","swapper/5",0
-6824711858020000,1,151000,6824711858171000,41,"S",97,35,"app",678
+6824711858020000,1,151000,6824711858171000,773,"S",97,493,"app",678
 6824711858171000,1,29595000,6824711887766000,0,"R",120,"[NULL]","swapper/5",0
-6824711858291000,0,79000,6824711858370000,40,"S",97,35,"DispSync",676
+6824711858291000,0,79000,6824711858370000,771,"S",97,493,"DispSync",676
 6824711858370000,0,1693000,6824711860063000,0,"R",120,"[NULL]","swapper/5",0
-6824711860063000,0,48000,6824711860111000,44,"S",120,37,"ksoftirqd/0",3
+6824711860063000,0,48000,6824711860111000,3,"S",120,3,"ksoftirqd/0",3
 6824711860111000,0,5836000,6824711865947000,0,"R",120,"[NULL]","swapper/5",0
-6824711860779000,6,128000,6824711860907000,22,"S",49,20,"sugov:4",606
-6824711860907000,6,78000,6824711860985000,6,"S",120,4,"rcu_preempt",7
-6824711860985000,6,67000,6824711861052000,5,"S",120,7,"rcu_sched",8
-6824711861052000,6,50000,6824711861102000,27,"S",120,25,"rcuos/4",45
-6824711861102000,6,168000,6824711861270000,9,"S",120,6,"rcuop/4",44
-6824711861270000,6,32000,6824711861302000,8,"S",120,8,"rcuop/6",60
+6824711860779000,6,128000,6824711860907000,483,"S",49,483,"sugov:4",606
+6824711860907000,6,78000,6824711860985000,5,"S",120,5,"rcu_preempt",7
+6824711860985000,6,67000,6824711861052000,6,"S",120,6,"rcu_sched",8
+6824711861052000,6,50000,6824711861102000,39,"S",120,39,"rcuos/4",45
+6824711861102000,6,168000,6824711861270000,38,"S",120,38,"rcuop/4",44
+6824711861270000,6,32000,6824711861302000,52,"S",120,52,"rcuop/6",60
 6824711861302000,6,12006000,6824711873308000,0,"R",120,"[NULL]","swapper/5",0
-6824711861384000,2,457000,6824711861841000,10,"S",120,11,"rcuop/5",52
+6824711861384000,2,457000,6824711861841000,45,"S",120,45,"rcuop/5",52
 6824711861841000,2,27907000,6824711889748000,0,"R",120,"[NULL]","swapper/5",0
-6824711865947000,0,139000,6824711866086000,26,"S",49,23,"sugov:0",605
-6824711866086000,0,46000,6824711866132000,44,"S",120,37,"ksoftirqd/0",3
+6824711865947000,0,139000,6824711866086000,482,"S",49,482,"sugov:0",605
+6824711866086000,0,46000,6824711866132000,3,"S",120,3,"ksoftirqd/0",3
 6824711866132000,0,889000,6824711867021000,0,"R",120,"[NULL]","swapper/5",0
-6824711867021000,0,47000,6824711867068000,44,"S",120,37,"ksoftirqd/0",3
+6824711867021000,0,47000,6824711867068000,3,"S",120,3,"ksoftirqd/0",3
 6824711867068000,0,702000,6824711867770000,0,"R",120,"[NULL]","swapper/5",0
-6824711867770000,0,70000,6824711867840000,6,"S",120,4,"rcu_preempt",7
+6824711867770000,0,70000,6824711867840000,5,"S",120,5,"rcu_preempt",7
 6824711867840000,0,4623000,6824711872463000,0,"R",120,"[NULL]","swapper/5",0
-6824711872463000,0,52000,6824711872515000,44,"S",120,37,"ksoftirqd/0",3
+6824711872463000,0,52000,6824711872515000,3,"S",120,3,"ksoftirqd/0",3
 6824711872515000,0,868000,6824711873383000,0,"R",120,"[NULL]","swapper/5",0
-6824711873308000,6,68000,6824711873376000,16,"D",120,14,"kworker/u16:7",19422
+6824711873308000,6,68000,6824711873376000,702,"D",120,702,"kworker/u16:7",19422
 6824711873376000,6,665000,6824711874041000,0,"R",120,"[NULL]","swapper/5",0
-6824711873383000,0,136000,6824711873519000,6,"S",120,4,"rcu_preempt",7
-6824711873519000,0,76000,6824711873595000,23,"S",120,21,"rcuop/2",28
-6824711873595000,0,115000,6824711873710000,35,"S",120,33,"kworker/0:5",20371
-6824711873710000,0,46000,6824711873756000,11,"S",120,9,"rcuop/0",10
+6824711873383000,0,136000,6824711873519000,5,"S",120,5,"rcu_preempt",7
+6824711873519000,0,76000,6824711873595000,24,"S",120,24,"rcuop/2",28
+6824711873595000,0,115000,6824711873710000,743,"S",120,743,"kworker/0:5",20371
+6824711873710000,0,46000,6824711873756000,8,"S",120,8,"rcuop/0",10
 6824711873756000,0,6329000,6824711880085000,0,"R",120,"[NULL]","swapper/5",0
-6824711874041000,6,477000,6824711874518000,16,"D",120,14,"kworker/u16:7",19422
+6824711874041000,6,477000,6824711874518000,702,"D",120,702,"kworker/u16:7",19422
 6824711874518000,6,672000,6824711875190000,0,"R",120,"[NULL]","swapper/5",0
-6824711874600000,3,138000,6824711874738000,32,"S",120,30,"smem_native_rpm",87
+6824711874600000,3,138000,6824711874738000,77,"S",120,77,"smem_native_rpm",87
 6824711874738000,3,920000,6824711875658000,0,"R",120,"[NULL]","swapper/5",0
-6824711875190000,6,207000,6824711875397000,16,"D",120,14,"kworker/u16:7",19422
+6824711875190000,6,207000,6824711875397000,702,"D",120,702,"kworker/u16:7",19422
 6824711875397000,6,667000,6824711876064000,0,"R",120,"[NULL]","swapper/5",0
-6824711875658000,3,86000,6824711875744000,32,"S",120,30,"smem_native_rpm",87
+6824711875658000,3,86000,6824711875744000,77,"S",120,77,"smem_native_rpm",87
 6824711875744000,3,729000,6824711876473000,0,"R",120,"[NULL]","swapper/5",0
-6824711876064000,6,202000,6824711876266000,16,"D",120,14,"kworker/u16:7",19422
+6824711876064000,6,202000,6824711876266000,702,"D",120,702,"kworker/u16:7",19422
 6824711876266000,6,110000,6824711876376000,0,"R",120,"[NULL]","swapper/5",0
-6824711876376000,6,96000,6824711876472000,16,"D",120,14,"kworker/u16:7",19422
+6824711876376000,6,96000,6824711876472000,702,"D",120,702,"kworker/u16:7",19422
 6824711876472000,6,704000,6824711877176000,0,"R",120,"[NULL]","swapper/5",0
-6824711876473000,3,71000,6824711876544000,32,"S",120,30,"smem_native_rpm",87
+6824711876473000,3,71000,6824711876544000,77,"S",120,77,"smem_native_rpm",87
 6824711876544000,3,783000,6824711877327000,0,"R",120,"[NULL]","swapper/5",0
-6824711877176000,6,125000,6824711877301000,16,"D",120,14,"kworker/u16:7",19422
+6824711877176000,6,125000,6824711877301000,702,"D",120,702,"kworker/u16:7",19422
 6824711877301000,6,678000,6824711877979000,0,"R",120,"[NULL]","swapper/5",0
-6824711877327000,3,143000,6824711877470000,32,"S",120,30,"smem_native_rpm",87
+6824711877327000,3,143000,6824711877470000,77,"S",120,77,"smem_native_rpm",87
 6824711877470000,3,716000,6824711878186000,0,"R",120,"[NULL]","swapper/5",0
-6824711877979000,6,198000,6824711878177000,16,"D",120,14,"kworker/u16:7",19422
+6824711877979000,6,198000,6824711878177000,702,"D",120,702,"kworker/u16:7",19422
 6824711878177000,6,472000,6824711878649000,0,"R",120,"[NULL]","swapper/5",0
-6824711878186000,3,193000,6824711878379000,32,"S",120,30,"smem_native_rpm",87
+6824711878186000,3,193000,6824711878379000,77,"S",120,77,"smem_native_rpm",87
 6824711878379000,3,469000,6824711878848000,0,"R",120,"[NULL]","swapper/5",0
-6824711878649000,6,182000,6824711878831000,16,"D",120,14,"kworker/u16:7",19422
+6824711878649000,6,182000,6824711878831000,702,"D",120,702,"kworker/u16:7",19422
 6824711878831000,6,446000,6824711879277000,0,"R",120,"[NULL]","swapper/5",0
-6824711878848000,3,140000,6824711878988000,32,"S",120,30,"smem_native_rpm",87
+6824711878848000,3,140000,6824711878988000,77,"S",120,77,"smem_native_rpm",87
 6824711878988000,3,666000,6824711879654000,0,"R",120,"[NULL]","swapper/5",0
-6824711879277000,6,161000,6824711879438000,16,"D",120,14,"kworker/u16:7",19422
+6824711879277000,6,161000,6824711879438000,702,"D",120,702,"kworker/u16:7",19422
 6824711879438000,6,112000,6824711879550000,0,"R",120,"[NULL]","swapper/5",0
-6824711879550000,6,82000,6824711879632000,16,"D",120,14,"kworker/u16:7",19422
+6824711879550000,6,82000,6824711879632000,702,"D",120,702,"kworker/u16:7",19422
 6824711879632000,6,169000,6824711879801000,0,"R",120,"[NULL]","swapper/5",0
-6824711879654000,3,182000,6824711879836000,32,"S",120,30,"smem_native_rpm",87
-6824711879801000,6,192000,6824711879993000,16,"D",120,14,"kworker/u16:7",19422
+6824711879654000,3,182000,6824711879836000,77,"S",120,77,"smem_native_rpm",87
+6824711879801000,6,192000,6824711879993000,702,"D",120,702,"kworker/u16:7",19422
 6824711879836000,3,398000,6824711880234000,0,"R",120,"[NULL]","swapper/5",0
 6824711879993000,6,231000,6824711880224000,0,"R",120,"[NULL]","swapper/5",0
-6824711880085000,0,155000,6824711880240000,44,"S",120,37,"ksoftirqd/0",3
-6824711880224000,6,143000,6824711880367000,16,"D",120,14,"kworker/u16:7",19422
-6824711880234000,3,64000,6824711880298000,32,"S",120,30,"smem_native_rpm",87
-6824711880240000,0,57000,6824711880297000,6,"S",120,4,"rcu_preempt",7
+6824711880085000,0,155000,6824711880240000,3,"S",120,3,"ksoftirqd/0",3
+6824711880224000,6,143000,6824711880367000,702,"D",120,702,"kworker/u16:7",19422
+6824711880234000,3,64000,6824711880298000,77,"S",120,77,"smem_native_rpm",87
+6824711880240000,0,57000,6824711880297000,5,"S",120,5,"rcu_preempt",7
 6824711880297000,0,6382000,6824711886679000,0,"R",120,"[NULL]","swapper/5",0
 6824711880298000,3,94000,6824711880392000,0,"R",120,"[NULL]","swapper/5",0
 6824711880367000,6,186000,6824711880553000,0,"R",120,"[NULL]","swapper/5",0
-6824711880392000,3,202000,6824711880594000,32,"S",120,30,"smem_native_rpm",87
-6824711880553000,6,58000,6824711880611000,16,"S",120,14,"kworker/u16:7",19422
+6824711880392000,3,202000,6824711880594000,77,"S",120,77,"smem_native_rpm",87
+6824711880553000,6,58000,6824711880611000,702,"S",120,702,"kworker/u16:7",19422
 6824711880594000,3,14276000,6824711894870000,0,"R",120,"[NULL]","swapper/5",0
 6824711880611000,6,5024000,6824711885635000,0,"R",120,"[NULL]","swapper/5",0
-6824711885635000,6,44000,6824711885679000,45,"S",120,38,"ksoftirqd/6",57
+6824711885635000,6,44000,6824711885679000,50,"S",120,50,"ksoftirqd/6",57
 6824711885679000,6,8649000,6824711894328000,0,"R",120,"[NULL]","swapper/5",0
-6824711886679000,0,139000,6824711886818000,6,"S",120,4,"rcu_preempt",7
-6824711886818000,0,59000,6824711886877000,8,"S",120,8,"rcuop/6",60
+6824711886679000,0,139000,6824711886818000,5,"S",120,5,"rcu_preempt",7
+6824711886818000,0,59000,6824711886877000,52,"S",120,52,"rcuop/6",60
 6824711886877000,0,585000,6824711887462000,0,"R",120,"[NULL]","swapper/5",0
-6824711887462000,0,118000,6824711887580000,35,"S",120,33,"kworker/0:5",20371
+6824711887462000,0,118000,6824711887580000,743,"S",120,743,"kworker/0:5",20371
 6824711887580000,0,455000,6824711888035000,0,"R",120,"[NULL]","swapper/5",0
-6824711887766000,1,377000,6824711888143000,36,"S",111,34,"SDM_EventThread",685
-6824711888035000,0,327000,6824711888362000,38,"S",120,35,"HwBinder:640_1",721
+6824711887766000,1,377000,6824711888143000,786,"S",111,494,"SDM_EventThread",685
+6824711888035000,0,327000,6824711888362000,777,"S",120,493,"HwBinder:640_1",721
 6824711888143000,1,88000,6824711888231000,0,"R",120,"[NULL]","swapper/5",0
-6824711888231000,1,85000,6824711888316000,40,"S",97,35,"DispSync",676
+6824711888231000,1,85000,6824711888316000,771,"S",97,493,"DispSync",676
 6824711888316000,1,979000,6824711889295000,0,"R",120,"[NULL]","swapper/5",0
 6824711888362000,0,604000,6824711888966000,0,"R",120,"[NULL]","swapper/5",0
-6824711888966000,0,178000,6824711889144000,40,"S",97,35,"DispSync",676
+6824711888966000,0,178000,6824711889144000,771,"S",97,493,"DispSync",676
 6824711889144000,0,532000,6824711889676000,0,"R",120,"[NULL]","swapper/5",0
-6824711889295000,1,275000,6824711889570000,41,"S",97,35,"app",678
+6824711889295000,1,275000,6824711889570000,773,"S",97,493,"app",678
 6824711889570000,1,1823000,6824711891393000,0,"R",120,"[NULL]","swapper/5",0
-6824711889676000,0,1753000,6824711891429000,42,"S",120,36,"ndroid.systemui",1664
-6824711889748000,2,86000,6824711889834000,40,"S",97,35,"DispSync",676
+6824711889676000,0,1753000,6824711891429000,644,"S",120,644,"ndroid.systemui",1664
+6824711889748000,2,86000,6824711889834000,771,"S",97,493,"DispSync",676
 6824711889834000,2,34646000,6824711924480000,0,"R",120,"[NULL]","swapper/5",0
-6824711891393000,1,280000,6824711891673000,43,"S",120,35,"Binder:640_2",675
+6824711891393000,1,280000,6824711891673000,770,"S",120,493,"Binder:640_2",675
 6824711891429000,0,393000,6824711891822000,0,"R",120,"[NULL]","swapper/5",0
 6824711891673000,1,472000,6824711892145000,0,"R",120,"[NULL]","swapper/5",0
-6824711891822000,0,178000,6824711892000000,41,"S",97,35,"app",678
+6824711891822000,0,178000,6824711892000000,773,"S",97,493,"app",678
 6824711892000000,0,1451000,6824711893451000,0,"R",120,"[NULL]","swapper/5",0
-6824711892145000,1,95000,6824711892240000,40,"S",97,35,"DispSync",676
+6824711892145000,1,95000,6824711892240000,771,"S",97,493,"DispSync",676
 6824711892240000,1,12180000,6824711904420000,0,"R",120,"[NULL]","swapper/5",0
-6824711893451000,0,109000,6824711893560000,6,"S",120,4,"rcu_preempt",7
-6824711893560000,0,38000,6824711893598000,23,"S",120,21,"rcuop/2",28
-6824711893598000,0,52000,6824711893650000,11,"S",120,9,"rcuop/0",10
-6824711893650000,0,78000,6824711893728000,35,"D",120,33,"kworker/0:5",20371
-6824711893728000,0,65000,6824711893793000,46,"S",120,39,"kworker/0:1",19511
-6824711893793000,0,59000,6824711893852000,35,"S",120,33,"kworker/0:5",20371
+6824711893451000,0,109000,6824711893560000,5,"S",120,5,"rcu_preempt",7
+6824711893560000,0,38000,6824711893598000,24,"S",120,24,"rcuop/2",28
+6824711893598000,0,52000,6824711893650000,8,"S",120,8,"rcuop/0",10
+6824711893650000,0,78000,6824711893728000,743,"D",120,743,"kworker/0:5",20371
+6824711893728000,0,65000,6824711893793000,705,"S",120,705,"kworker/0:1",19511
+6824711893793000,0,59000,6824711893852000,743,"S",120,743,"kworker/0:5",20371
 6824711893852000,0,27555000,6824711921407000,0,"R",120,"[NULL]","swapper/5",0
-6824711894328000,6,300000,6824711894628000,16,"D",120,14,"kworker/u16:7",19422
+6824711894328000,6,300000,6824711894628000,702,"D",120,702,"kworker/u16:7",19422
 6824711894628000,6,2368000,6824711896996000,0,"R",120,"[NULL]","swapper/5",0
-6824711894870000,3,77000,6824711894947000,32,"S",120,30,"smem_native_rpm",87
+6824711894870000,3,77000,6824711894947000,77,"S",120,77,"smem_native_rpm",87
 6824711894947000,3,4289000,6824711899236000,0,"R",120,"[NULL]","swapper/5",0
-6824711896996000,6,306000,6824711897302000,16,"D",120,14,"kworker/u16:7",19422
+6824711896996000,6,306000,6824711897302000,702,"D",120,702,"kworker/u16:7",19422
 6824711897302000,6,48000,6824711897350000,0,"R",120,"[NULL]","swapper/5",0
-6824711897350000,6,43000,6824711897393000,45,"S",120,38,"ksoftirqd/6",57
+6824711897350000,6,43000,6824711897393000,50,"S",120,50,"ksoftirqd/6",57
 6824711897393000,6,3585000,6824711900978000,0,"R",120,"[NULL]","swapper/5",0
-6824711899236000,3,257000,6824711899493000,32,"S",120,30,"smem_native_rpm",87
+6824711899236000,3,257000,6824711899493000,77,"S",120,77,"smem_native_rpm",87
 6824711899493000,3,4694000,6824711904187000,0,"R",120,"[NULL]","swapper/5",0
-6824711900978000,6,122000,6824711901100000,16,"S",120,14,"kworker/u16:7",19422
+6824711900978000,6,122000,6824711901100000,702,"S",120,702,"kworker/u16:7",19422
 6824711901100000,6,50000,6824711901150000,0,"R",120,"[NULL]","swapper/5",0
-6824711901150000,6,40000,6824711901190000,45,"S",120,38,"ksoftirqd/6",57
+6824711901150000,6,40000,6824711901190000,50,"S",120,50,"ksoftirqd/6",57
 6824711901190000,6,826872000,6824712728062000,0,"R",120,"[NULL]","swapper/5",0
-6824711904187000,3,212000,6824711904399000,32,"S",120,30,"smem_native_rpm",87
+6824711904187000,3,212000,6824711904399000,77,"S",120,77,"smem_native_rpm",87
 6824711904399000,3,55334000,6824711959733000,0,"R",120,"[NULL]","swapper/5",0
-6824711904420000,1,72000,6824711904492000,47,"S",120,40,"ksoftirqd/1",17
+6824711904420000,1,72000,6824711904492000,15,"S",120,15,"ksoftirqd/1",17
 6824711904492000,1,14769000,6824711919261000,0,"R",120,"[NULL]","swapper/5",0
-6824711919261000,1,145000,6824711919406000,47,"S",120,40,"ksoftirqd/1",17
+6824711919261000,1,145000,6824711919406000,15,"S",120,15,"ksoftirqd/1",17
 6824711919406000,1,3219000,6824711922625000,0,"R",120,"[NULL]","swapper/5",0
-6824711921407000,0,185000,6824711921592000,35,"S",120,33,"kworker/0:5",20371
-6824711921592000,0,726000,6824711922318000,36,"S",111,34,"SDM_EventThread",685
+6824711921407000,0,185000,6824711921592000,743,"S",120,743,"kworker/0:5",20371
+6824711921592000,0,726000,6824711922318000,786,"S",111,494,"SDM_EventThread",685
 6824711922318000,0,65000,6824711922383000,0,"R",120,"[NULL]","swapper/5",0
-6824711922383000,0,57000,6824711922440000,44,"S",120,37,"ksoftirqd/0",3
+6824711922383000,0,57000,6824711922440000,3,"S",120,3,"ksoftirqd/0",3
 6824711922440000,0,585000,6824711923025000,0,"R",120,"[NULL]","swapper/5",0
-6824711922625000,1,477000,6824711923102000,38,"S",120,35,"HwBinder:640_1",721
-6824711923025000,0,239000,6824711923264000,40,"S",97,35,"DispSync",676
+6824711922625000,1,477000,6824711923102000,777,"S",120,493,"HwBinder:640_1",721
+6824711923025000,0,239000,6824711923264000,771,"S",97,493,"DispSync",676
 6824711923102000,1,584000,6824711923686000,0,"R",120,"[NULL]","swapper/5",0
 6824711923264000,0,1073000,6824711924337000,0,"R",120,"[NULL]","swapper/5",0
-6824711923686000,1,409000,6824711924095000,41,"S",97,35,"app",678
+6824711923686000,1,409000,6824711924095000,773,"S",97,493,"app",678
 6824711924095000,1,2591000,6824711926686000,0,"R",120,"[NULL]","swapper/5",0
-6824711924337000,0,2416000,6824711926753000,42,"S",120,36,"ndroid.systemui",1664
-6824711924480000,2,93000,6824711924573000,40,"S",97,35,"DispSync",676
+6824711924337000,0,2416000,6824711926753000,644,"S",120,644,"ndroid.systemui",1664
+6824711924480000,2,93000,6824711924573000,771,"S",97,493,"DispSync",676
 6824711924573000,2,34369000,6824711958942000,0,"R",120,"[NULL]","swapper/5",0
-6824711926686000,1,341000,6824711927027000,43,"S",120,35,"Binder:640_2",675
+6824711926686000,1,341000,6824711927027000,770,"S",120,493,"Binder:640_2",675
 6824711926753000,0,601000,6824711927354000,0,"R",120,"[NULL]","swapper/5",0
 6824711927027000,1,795000,6824711927822000,0,"R",120,"[NULL]","swapper/5",0
-6824711927354000,0,186000,6824711927540000,41,"S",97,35,"app",678
+6824711927354000,0,186000,6824711927540000,773,"S",97,493,"app",678
 6824711927540000,0,30747000,6824711958287000,0,"R",120,"[NULL]","swapper/5",0
-6824711927822000,1,86000,6824711927908000,40,"S",97,35,"DispSync",676
+6824711927822000,1,86000,6824711927908000,771,"S",97,493,"DispSync",676
 6824711927908000,1,30404000,6824711958312000,0,"R",120,"[NULL]","swapper/5",0
-6824711935090000,5,59000,6824711935149000,21,"S",120,19,"kswapd0",150
+6824711935090000,5,59000,6824711935149000,113,"S",120,113,"kswapd0",150
 6824711935149000,5,791709000,6824712726858000,0,"R",120,"[NULL]","swapper/5",0
-6824711958287000,0,325000,6824711958612000,40,"S",97,35,"DispSync",676
-6824711958312000,1,275000,6824711958587000,47,"S",120,40,"ksoftirqd/1",17
-6824711958587000,1,120000,6824711958707000,48,"S",120,41,"kworker/1:1",18800
-6824711958612000,0,107000,6824711958719000,35,"S",120,33,"kworker/0:5",20371
+6824711958287000,0,325000,6824711958612000,771,"S",97,493,"DispSync",676
+6824711958312000,1,275000,6824711958587000,15,"S",120,15,"ksoftirqd/1",17
+6824711958587000,1,120000,6824711958707000,693,"S",120,693,"kworker/1:1",18800
+6824711958612000,0,107000,6824711958719000,743,"S",120,743,"kworker/0:5",20371
 6824711958707000,1,827000,6824711959534000,0,"R",120,"[NULL]","swapper/5",0
-6824711958719000,0,580000,6824711959299000,36,"S",111,34,"SDM_EventThread",685
-6824711958942000,2,402000,6824711959344000,41,"S",97,35,"app",678
-6824711959299000,0,1274000,6824711960573000,16,"R+",120,14,"kworker/u16:7",19422
+6824711958719000,0,580000,6824711959299000,786,"S",111,494,"SDM_EventThread",685
+6824711958942000,2,402000,6824711959344000,773,"S",97,493,"app",678
+6824711959299000,0,1274000,6824711960573000,702,"R+",120,702,"kworker/u16:7",19422
 6824711959344000,2,862000,6824711960206000,0,"R",120,"[NULL]","swapper/5",0
-6824711959534000,1,579000,6824711960113000,38,"S",120,35,"HwBinder:640_1",721
-6824711959733000,3,66000,6824711959799000,40,"S",97,35,"DispSync",676
-6824711959799000,3,2784000,6824711962583000,42,"S",120,36,"ndroid.systemui",1664
+6824711959534000,1,579000,6824711960113000,777,"S",120,493,"HwBinder:640_1",721
+6824711959733000,3,66000,6824711959799000,771,"S",97,493,"DispSync",676
+6824711959799000,3,2784000,6824711962583000,644,"S",120,644,"ndroid.systemui",1664
 6824711960113000,1,3163000,6824711963276000,0,"R",120,"[NULL]","swapper/5",0
-6824711960206000,2,86000,6824711960292000,40,"S",97,35,"DispSync",676
+6824711960206000,2,86000,6824711960292000,771,"S",97,493,"DispSync",676
 6824711960292000,2,31867000,6824711992159000,0,"R",120,"[NULL]","swapper/5",0
-6824711960573000,0,54000,6824711960627000,32,"S",120,30,"smem_native_rpm",87
-6824711960627000,0,52000,6824711960679000,16,"D",120,14,"kworker/u16:7",19422
+6824711960573000,0,54000,6824711960627000,77,"S",120,77,"smem_native_rpm",87
+6824711960627000,0,52000,6824711960679000,702,"D",120,702,"kworker/u16:7",19422
 6824711960679000,0,1939000,6824711962618000,0,"R",120,"[NULL]","swapper/5",0
-6824711962583000,3,212000,6824711962795000,16,"D",120,14,"kworker/u16:7",19422
-6824711962618000,0,399000,6824711963017000,43,"S",120,35,"Binder:640_2",675
+6824711962583000,3,212000,6824711962795000,702,"D",120,702,"kworker/u16:7",19422
+6824711962618000,0,399000,6824711963017000,770,"S",120,493,"Binder:640_2",675
 6824711962795000,3,29662000,6824711992457000,0,"R",120,"[NULL]","swapper/5",0
-6824711963017000,0,225000,6824711963242000,32,"S",120,30,"smem_native_rpm",87
+6824711963017000,0,225000,6824711963242000,77,"S",120,77,"smem_native_rpm",87
 6824711963242000,0,720000,6824711963962000,0,"R",120,"[NULL]","swapper/5",0
-6824711963276000,1,193000,6824711963469000,41,"S",97,35,"app",678
-6824711963469000,1,75000,6824711963544000,16,"S",120,14,"kworker/u16:7",19422
+6824711963276000,1,193000,6824711963469000,773,"S",97,493,"app",678
+6824711963469000,1,75000,6824711963544000,702,"S",120,702,"kworker/u16:7",19422
 6824711963544000,1,27817000,6824711991361000,0,"R",120,"[NULL]","swapper/5",0
-6824711963962000,0,93000,6824711964055000,40,"S",97,35,"DispSync",676
+6824711963962000,0,93000,6824711964055000,771,"S",97,493,"DispSync",676
 6824711964055000,0,26981000,6824711991036000,0,"R",120,"[NULL]","swapper/5",0
-6824711991036000,0,265000,6824711991301000,35,"S",120,33,"kworker/0:5",20371
-6824711991301000,0,902000,6824711992203000,36,"S",111,34,"SDM_EventThread",685
-6824711991361000,1,512000,6824711991873000,40,"S",97,35,"DispSync",676
+6824711991036000,0,265000,6824711991301000,743,"S",120,743,"kworker/0:5",20371
+6824711991301000,0,902000,6824711992203000,786,"S",111,494,"SDM_EventThread",685
+6824711991361000,1,512000,6824711991873000,771,"S",97,493,"DispSync",676
 6824711991873000,1,90000,6824711991963000,0,"R",120,"[NULL]","swapper/5",0
-6824711991963000,1,86000,6824711992049000,47,"S",120,40,"ksoftirqd/1",17
+6824711991963000,1,86000,6824711992049000,15,"S",120,15,"ksoftirqd/1",17
 6824711992049000,1,1109000,6824711993158000,0,"R",120,"[NULL]","swapper/5",0
-6824711992159000,2,539000,6824711992698000,41,"S",97,35,"app",678
+6824711992159000,2,539000,6824711992698000,773,"S",97,493,"app",678
 6824711992203000,0,811000,6824711993014000,0,"R",120,"[NULL]","swapper/5",0
-6824711992457000,3,561000,6824711993018000,38,"S",120,35,"HwBinder:640_1",721
+6824711992457000,3,561000,6824711993018000,777,"S",120,493,"HwBinder:640_1",721
 6824711992698000,2,31632000,6824712024330000,0,"R",120,"[NULL]","swapper/5",0
-6824711993014000,0,3047000,6824711996061000,42,"S",120,36,"ndroid.systemui",1664
+6824711993014000,0,3047000,6824711996061000,644,"S",120,644,"ndroid.systemui",1664
 6824711993018000,3,29607000,6824712022625000,0,"R",120,"[NULL]","swapper/5",0
-6824711993158000,1,88000,6824711993246000,40,"S",97,35,"DispSync",676
+6824711993158000,1,88000,6824711993246000,771,"S",97,493,"DispSync",676
 6824711993246000,1,2846000,6824711996092000,0,"R",120,"[NULL]","swapper/5",0
 6824711996061000,0,723000,6824711996784000,0,"R",120,"[NULL]","swapper/5",0
-6824711996092000,1,399000,6824711996491000,43,"S",120,35,"Binder:640_2",675
+6824711996092000,1,399000,6824711996491000,770,"S",120,493,"Binder:640_2",675
 6824711996491000,1,795000,6824711997286000,0,"R",120,"[NULL]","swapper/5",0
-6824711996784000,0,206000,6824711996990000,41,"S",97,35,"app",678
+6824711996784000,0,206000,6824711996990000,773,"S",97,493,"app",678
 6824711996990000,0,72000,6824711997062000,0,"R",120,"[NULL]","swapper/5",0
-6824711997062000,0,65000,6824711997127000,44,"S",120,37,"ksoftirqd/0",3
+6824711997062000,0,65000,6824711997127000,3,"S",120,3,"ksoftirqd/0",3
 6824711997127000,0,25255000,6824712022382000,0,"R",120,"[NULL]","swapper/5",0
-6824711997286000,1,89000,6824711997375000,40,"S",97,35,"DispSync",676
+6824711997286000,1,89000,6824711997375000,771,"S",97,493,"DispSync",676
 6824711997375000,1,26567000,6824712023942000,0,"R",120,"[NULL]","swapper/5",0
-6824712022382000,0,182000,6824712022564000,35,"S",120,33,"kworker/0:5",20371
-6824712022564000,0,1155000,6824712023719000,36,"S",111,34,"SDM_EventThread",685
-6824712022625000,3,91000,6824712022716000,49,"S",120,42,"kworker/3:1",17791
+6824712022382000,0,182000,6824712022564000,743,"S",120,743,"kworker/0:5",20371
+6824712022564000,0,1155000,6824712023719000,786,"S",111,494,"SDM_EventThread",685
+6824712022625000,3,91000,6824712022716000,686,"S",120,686,"kworker/3:1",17791
 6824712022716000,3,37690000,6824712060406000,0,"R",120,"[NULL]","swapper/5",0
-6824712023719000,0,311000,6824712024030000,16,"S",120,14,"kworker/u16:7",19422
-6824712023942000,1,639000,6824712024581000,38,"S",120,35,"HwBinder:640_1",721
+6824712023719000,0,311000,6824712024030000,702,"S",120,702,"kworker/u16:7",19422
+6824712023942000,1,639000,6824712024581000,777,"S",120,493,"HwBinder:640_1",721
 6824712024030000,0,921000,6824712024951000,0,"R",120,"[NULL]","swapper/5",0
-6824712024330000,2,324000,6824712024654000,40,"S",97,35,"DispSync",676
+6824712024330000,2,324000,6824712024654000,771,"S",97,493,"DispSync",676
 6824712024581000,1,1190000,6824712025771000,0,"R",120,"[NULL]","swapper/5",0
 6824712024654000,2,1208000,6824712025862000,0,"R",120,"[NULL]","swapper/5",0
-6824712024951000,0,531000,6824712025482000,41,"S",97,35,"app",678
+6824712024951000,0,531000,6824712025482000,773,"S",97,493,"app",678
 6824712025482000,0,3497000,6824712028979000,0,"R",120,"[NULL]","swapper/5",0
-6824712025771000,1,3163000,6824712028934000,42,"S",120,36,"ndroid.systemui",1664
-6824712025862000,2,83000,6824712025945000,40,"S",97,35,"DispSync",676
+6824712025771000,1,3163000,6824712028934000,644,"S",120,644,"ndroid.systemui",1664
+6824712025862000,2,83000,6824712025945000,771,"S",97,493,"DispSync",676
 6824712025945000,2,34179000,6824712060124000,0,"R",120,"[NULL]","swapper/5",0
 6824712028934000,1,717000,6824712029651000,0,"R",120,"[NULL]","swapper/5",0
-6824712028979000,0,393000,6824712029372000,43,"S",120,35,"Binder:640_2",675
+6824712028979000,0,393000,6824712029372000,770,"S",120,493,"Binder:640_2",675
 6824712029372000,0,798000,6824712030170000,0,"R",120,"[NULL]","swapper/5",0
-6824712029651000,1,204000,6824712029855000,41,"S",97,35,"app",678
+6824712029651000,1,204000,6824712029855000,773,"S",97,493,"app",678
 6824712029855000,1,29454000,6824712059309000,0,"R",120,"[NULL]","swapper/5",0
-6824712030170000,0,89000,6824712030259000,40,"S",97,35,"DispSync",676
+6824712030170000,0,89000,6824712030259000,771,"S",97,493,"DispSync",676
 6824712030259000,0,70000,6824712030329000,0,"R",120,"[NULL]","swapper/5",0
-6824712030329000,0,70000,6824712030399000,44,"S",120,37,"ksoftirqd/0",3
+6824712030329000,0,70000,6824712030399000,3,"S",120,3,"ksoftirqd/0",3
 6824712030399000,0,28593000,6824712058992000,0,"R",120,"[NULL]","swapper/5",0
-6824712058992000,0,265000,6824712059257000,35,"S",120,33,"kworker/0:5",20371
-6824712059257000,0,1001000,6824712060258000,36,"S",111,34,"SDM_EventThread",685
-6824712059309000,1,519000,6824712059828000,40,"S",97,35,"DispSync",676
+6824712058992000,0,265000,6824712059257000,743,"S",120,743,"kworker/0:5",20371
+6824712059257000,0,1001000,6824712060258000,786,"S",111,494,"SDM_EventThread",685
+6824712059309000,1,519000,6824712059828000,771,"S",97,493,"DispSync",676
 6824712059828000,1,364000,6824712060192000,0,"R",120,"[NULL]","swapper/5",0
-6824712060124000,2,513000,6824712060637000,41,"S",97,35,"app",678
-6824712060192000,1,37000,6824712060229000,47,"S",120,40,"ksoftirqd/1",17
-6824712060229000,1,111000,6824712060340000,48,"S",120,41,"kworker/1:1",18800
-6824712060258000,0,735000,6824712060993000,16,"R+",120,14,"kworker/u16:7",19422
+6824712060124000,2,513000,6824712060637000,773,"S",97,493,"app",678
+6824712060192000,1,37000,6824712060229000,15,"S",120,15,"ksoftirqd/1",17
+6824712060229000,1,111000,6824712060340000,693,"S",120,693,"kworker/1:1",18800
+6824712060258000,0,735000,6824712060993000,702,"R+",120,702,"kworker/u16:7",19422
 6824712060340000,1,928000,6824712061268000,0,"R",120,"[NULL]","swapper/5",0
-6824712060406000,3,546000,6824712060952000,38,"S",120,35,"HwBinder:640_1",721
+6824712060406000,3,546000,6824712060952000,777,"S",120,493,"HwBinder:640_1",721
 6824712060637000,2,31702000,6824712092339000,0,"R",120,"[NULL]","swapper/5",0
 6824712060952000,3,31540000,6824712092492000,0,"R",120,"[NULL]","swapper/5",0
-6824712060993000,0,65000,6824712061058000,40,"S",97,35,"DispSync",676
-6824712061058000,0,280000,6824712061338000,16,"S",120,14,"kworker/u16:7",19422
-6824712061268000,1,3172000,6824712064440000,42,"S",120,36,"ndroid.systemui",1664
+6824712060993000,0,65000,6824712061058000,771,"S",97,493,"DispSync",676
+6824712061058000,0,280000,6824712061338000,702,"S",120,702,"kworker/u16:7",19422
+6824712061268000,1,3172000,6824712064440000,644,"S",120,644,"ndroid.systemui",1664
 6824712061338000,0,3134000,6824712064472000,0,"R",120,"[NULL]","swapper/5",0
 6824712064440000,1,710000,6824712065150000,0,"R",120,"[NULL]","swapper/5",0
-6824712064472000,0,400000,6824712064872000,43,"S",120,35,"Binder:640_2",675
+6824712064472000,0,400000,6824712064872000,770,"S",120,493,"Binder:640_2",675
 6824712064872000,0,780000,6824712065652000,0,"R",120,"[NULL]","swapper/5",0
-6824712065150000,1,201000,6824712065351000,41,"S",97,35,"app",678
+6824712065150000,1,201000,6824712065351000,773,"S",97,493,"app",678
 6824712065351000,1,10437000,6824712075788000,0,"R",120,"[NULL]","swapper/5",0
-6824712065652000,0,89000,6824712065741000,40,"S",97,35,"DispSync",676
+6824712065652000,0,89000,6824712065741000,771,"S",97,493,"DispSync",676
 6824712065741000,0,24857000,6824712090598000,0,"R",120,"[NULL]","swapper/5",0
-6824712075788000,1,292000,6824712076080000,50,"S",120,43,"Executor-7",14762
+6824712075788000,1,292000,6824712076080000,1919,"S",120,667,"Executor-7",14762
 6824712076080000,1,88000,6824712076168000,0,"R",120,"[NULL]","swapper/5",0
-6824712076168000,1,225000,6824712076393000,47,"S",120,40,"ksoftirqd/1",17
-6824712076393000,1,162000,6824712076555000,16,"S",120,14,"kworker/u16:7",19422
+6824712076168000,1,225000,6824712076393000,15,"S",120,15,"ksoftirqd/1",17
+6824712076393000,1,162000,6824712076555000,702,"S",120,702,"kworker/u16:7",19422
 6824712076555000,1,15181000,6824712091736000,0,"R",120,"[NULL]","swapper/5",0
-6824712090598000,0,224000,6824712090822000,35,"S",120,33,"kworker/0:5",20371
-6824712090822000,0,1321000,6824712092143000,36,"S",111,34,"SDM_EventThread",685
-6824712091736000,1,410000,6824712092146000,40,"S",97,35,"DispSync",676
+6824712090598000,0,224000,6824712090822000,743,"S",120,743,"kworker/0:5",20371
+6824712090822000,0,1321000,6824712092143000,786,"S",111,494,"SDM_EventThread",685
+6824712091736000,1,410000,6824712092146000,771,"S",97,493,"DispSync",676
 6824712092143000,0,90000,6824712092233000,0,"R",120,"[NULL]","swapper/5",0
 6824712092146000,1,1144000,6824712093290000,0,"R",120,"[NULL]","swapper/5",0
-6824712092233000,0,79000,6824712092312000,44,"S",120,37,"ksoftirqd/0",3
+6824712092233000,0,79000,6824712092312000,3,"S",120,3,"ksoftirqd/0",3
 6824712092312000,0,820000,6824712093132000,0,"R",120,"[NULL]","swapper/5",0
-6824712092339000,2,602000,6824712092941000,38,"S",120,35,"HwBinder:640_1",721
-6824712092492000,3,487000,6824712092979000,41,"S",97,35,"app",678
+6824712092339000,2,602000,6824712092941000,777,"S",120,493,"HwBinder:640_1",721
+6824712092492000,3,487000,6824712092979000,773,"S",97,493,"app",678
 6824712092941000,2,4352000,6824712097293000,0,"R",120,"[NULL]","swapper/5",0
 6824712092979000,3,33505000,6824712126484000,0,"R",120,"[NULL]","swapper/5",0
-6824712093132000,0,90000,6824712093222000,40,"S",97,35,"DispSync",676
+6824712093132000,0,90000,6824712093222000,771,"S",97,493,"DispSync",676
 6824712093222000,0,3125000,6824712096347000,0,"R",120,"[NULL]","swapper/5",0
-6824712093290000,1,3028000,6824712096318000,42,"S",120,36,"ndroid.systemui",1664
+6824712093290000,1,3028000,6824712096318000,644,"S",120,644,"ndroid.systemui",1664
 6824712096318000,1,443000,6824712096761000,0,"R",120,"[NULL]","swapper/5",0
-6824712096347000,0,516000,6824712096863000,43,"S",120,35,"Binder:640_2",675
-6824712096761000,1,208000,6824712096969000,41,"S",97,35,"app",678
+6824712096347000,0,516000,6824712096863000,770,"S",120,493,"Binder:640_2",675
+6824712096761000,1,208000,6824712096969000,773,"S",97,493,"app",678
 6824712096863000,0,27799000,6824712124662000,0,"R",120,"[NULL]","swapper/5",0
 6824712096969000,1,28547000,6824712125516000,0,"R",120,"[NULL]","swapper/5",0
-6824712097293000,2,94000,6824712097387000,40,"S",97,35,"DispSync",676
+6824712097293000,2,94000,6824712097387000,771,"S",97,493,"DispSync",676
 6824712097387000,2,27295000,6824712124682000,0,"R",120,"[NULL]","swapper/5",0
-6824712124662000,0,474000,6824712125136000,40,"S",97,35,"DispSync",676
-6824712124682000,2,304000,6824712124986000,25,"S",120,24,"ksoftirqd/2",25
+6824712124662000,0,474000,6824712125136000,771,"S",97,493,"DispSync",676
+6824712124682000,2,304000,6824712124986000,22,"S",120,22,"ksoftirqd/2",25
 6824712124986000,2,1342000,6824712126328000,0,"R",120,"[NULL]","swapper/5",0
-6824712125136000,0,150000,6824712125286000,35,"S",120,33,"kworker/0:5",20371
-6824712125286000,0,705000,6824712125991000,36,"R+",111,34,"SDM_EventThread",685
-6824712125516000,1,532000,6824712126048000,41,"S",97,35,"app",678
-6824712125991000,0,50000,6824712126041000,40,"S",97,35,"DispSync",676
-6824712126041000,0,199000,6824712126240000,36,"S",111,34,"SDM_EventThread",685
+6824712125136000,0,150000,6824712125286000,743,"S",120,743,"kworker/0:5",20371
+6824712125286000,0,705000,6824712125991000,786,"R+",111,494,"SDM_EventThread",685
+6824712125516000,1,532000,6824712126048000,773,"S",97,493,"app",678
+6824712125991000,0,50000,6824712126041000,771,"S",97,493,"DispSync",676
+6824712126041000,0,199000,6824712126240000,786,"S",111,494,"SDM_EventThread",685
 6824712126048000,1,3632000,6824712129680000,0,"R",120,"[NULL]","swapper/5",0
-6824712126240000,0,149000,6824712126389000,16,"S",120,14,"kworker/u16:7",19422
-6824712126328000,2,2927000,6824712129255000,42,"S",120,36,"ndroid.systemui",1664
+6824712126240000,0,149000,6824712126389000,702,"S",120,702,"kworker/u16:7",19422
+6824712126328000,2,2927000,6824712129255000,644,"S",120,644,"ndroid.systemui",1664
 6824712126389000,0,970000,6824712127359000,0,"R",120,"[NULL]","swapper/5",0
-6824712126484000,3,712000,6824712127196000,38,"S",120,35,"HwBinder:640_1",721
+6824712126484000,3,712000,6824712127196000,777,"S",120,493,"HwBinder:640_1",721
 6824712127196000,3,32061000,6824712159257000,0,"R",120,"[NULL]","swapper/5",0
-6824712127359000,0,84000,6824712127443000,40,"S",97,35,"DispSync",676
+6824712127359000,0,84000,6824712127443000,771,"S",97,493,"DispSync",676
 6824712127443000,0,1769000,6824712129212000,0,"R",120,"[NULL]","swapper/5",0
-6824712129212000,0,299000,6824712129511000,43,"S",120,35,"Binder:640_2",675
+6824712129212000,0,299000,6824712129511000,770,"S",120,493,"Binder:640_2",675
 6824712129255000,2,29843000,6824712159098000,0,"R",120,"[NULL]","swapper/5",0
 6824712129511000,0,530000,6824712130041000,0,"R",120,"[NULL]","swapper/5",0
-6824712129680000,1,177000,6824712129857000,41,"S",97,35,"app",678
+6824712129680000,1,177000,6824712129857000,773,"S",97,493,"app",678
 6824712129857000,1,28493000,6824712158350000,0,"R",120,"[NULL]","swapper/5",0
-6824712130041000,0,88000,6824712130129000,40,"S",97,35,"DispSync",676
+6824712130041000,0,88000,6824712130129000,771,"S",97,493,"DispSync",676
 6824712130129000,0,64000,6824712130193000,0,"R",120,"[NULL]","swapper/5",0
-6824712130193000,0,56000,6824712130249000,44,"S",120,37,"ksoftirqd/0",3
+6824712130193000,0,56000,6824712130249000,3,"S",120,3,"ksoftirqd/0",3
 6824712130249000,0,496000,6824712130745000,0,"R",120,"[NULL]","swapper/5",0
-6824712130745000,0,331000,6824712131076000,16,"R+",120,14,"kworker/u16:7",19422
-6824712131076000,0,202000,6824712131278000,52,"D",120,44,"rild",1339
-6824712131278000,0,65000,6824712131343000,16,"R+",120,14,"kworker/u16:7",19422
-6824712131343000,0,671000,6824712132014000,52,"S",120,44,"rild",1339
-6824712131989000,7,56000,6824712132045000,17,"S",120,16,"kworker/u16:2",19725
-6824712132014000,0,442000,6824712132456000,54,"S",120,44,"rild",1260
+6824712130745000,0,331000,6824712131076000,702,"R+",120,702,"kworker/u16:7",19422
+6824712131076000,0,202000,6824712131278000,1199,"D",120,611,"rild",1339
+6824712131278000,0,65000,6824712131343000,702,"R+",120,702,"kworker/u16:7",19422
+6824712131343000,0,671000,6824712132014000,1199,"S",120,611,"rild",1339
+6824712131989000,7,56000,6824712132045000,711,"S",120,711,"kworker/u16:2",19725
+6824712132014000,0,442000,6824712132456000,1187,"S",120,611,"rild",1260
 6824712132045000,7,62353000,6824712194398000,0,"R",120,"[NULL]","swapper/5",0
-6824712132456000,0,749000,6824712133205000,55,"S",120,45,"suspend@1.0-ser",796
-6824712133205000,0,1823000,6824712135028000,54,"S",120,44,"rild",1260
-6824712135028000,0,81000,6824712135109000,11,"S",120,9,"rcuop/0",10
-6824712135109000,0,37000,6824712135146000,6,"S",120,4,"rcu_preempt",7
-6824712135146000,0,240000,6824712135386000,55,"S",120,45,"suspend@1.0-ser",796
-6824712135386000,0,65000,6824712135451000,16,"S",120,14,"kworker/u16:7",19422
+6824712132456000,0,749000,6824712133205000,543,"S",120,543,"suspend@1.0-ser",796
+6824712133205000,0,1823000,6824712135028000,1187,"S",120,611,"rild",1260
+6824712135028000,0,81000,6824712135109000,8,"S",120,8,"rcuop/0",10
+6824712135109000,0,37000,6824712135146000,5,"S",120,5,"rcu_preempt",7
+6824712135146000,0,240000,6824712135386000,543,"S",120,543,"suspend@1.0-ser",796
+6824712135386000,0,65000,6824712135451000,702,"S",120,702,"kworker/u16:7",19422
 6824712135451000,0,5153000,6824712140604000,0,"R",120,"[NULL]","swapper/5",0
-6824712140604000,0,103000,6824712140707000,6,"S",120,4,"rcu_preempt",7
-6824712140707000,0,133000,6824712140840000,11,"S",120,9,"rcuop/0",10
+6824712140604000,0,103000,6824712140707000,5,"S",120,5,"rcu_preempt",7
+6824712140707000,0,133000,6824712140840000,8,"S",120,8,"rcuop/0",10
 6824712140840000,0,16732000,6824712157572000,0,"R",120,"[NULL]","swapper/5",0
-6824712157572000,0,451000,6824712158023000,35,"S",120,33,"kworker/0:5",20371
-6824712158023000,0,992000,6824712159015000,36,"S",111,34,"SDM_EventThread",685
-6824712158350000,1,452000,6824712158802000,40,"S",97,35,"DispSync",676
+6824712157572000,0,451000,6824712158023000,743,"S",120,743,"kworker/0:5",20371
+6824712158023000,0,992000,6824712159015000,786,"S",111,494,"SDM_EventThread",685
+6824712158350000,1,452000,6824712158802000,771,"S",97,493,"DispSync",676
 6824712158802000,1,87000,6824712158889000,0,"R",120,"[NULL]","swapper/5",0
-6824712158889000,1,87000,6824712158976000,47,"S",120,40,"ksoftirqd/1",17
+6824712158889000,1,87000,6824712158976000,15,"S",120,15,"ksoftirqd/1",17
 6824712158976000,1,981000,6824712159957000,0,"R",120,"[NULL]","swapper/5",0
 6824712159015000,0,478000,6824712159493000,0,"R",120,"[NULL]","swapper/5",0
-6824712159098000,2,521000,6824712159619000,41,"S",97,35,"app",678
-6824712159257000,3,559000,6824712159816000,38,"S",120,35,"HwBinder:640_1",721
-6824712159493000,0,708000,6824712160201000,42,"R",120,36,"ndroid.systemui",1664
+6824712159098000,2,521000,6824712159619000,773,"S",97,493,"app",678
+6824712159257000,3,559000,6824712159816000,777,"S",120,493,"HwBinder:640_1",721
+6824712159493000,0,708000,6824712160201000,644,"R",120,644,"ndroid.systemui",1664
 6824712159619000,2,33324000,6824712192943000,0,"R",120,"[NULL]","swapper/5",0
 6824712159816000,3,33271000,6824712193087000,0,"R",120,"[NULL]","swapper/5",0
-6824712159957000,1,87000,6824712160044000,40,"S",97,35,"DispSync",676
+6824712159957000,1,87000,6824712160044000,771,"S",97,493,"DispSync",676
 6824712160044000,1,3767000,6824712163811000,0,"R",120,"[NULL]","swapper/5",0
-6824712160201000,0,878000,6824712161079000,16,"S",120,14,"kworker/u16:7",19422
-6824712161079000,0,86000,6824712161165000,35,"S",120,33,"kworker/0:5",20371
-6824712161165000,0,2618000,6824712163783000,42,"S",120,36,"ndroid.systemui",1664
+6824712160201000,0,878000,6824712161079000,702,"S",120,702,"kworker/u16:7",19422
+6824712161079000,0,86000,6824712161165000,743,"S",120,743,"kworker/0:5",20371
+6824712161165000,0,2618000,6824712163783000,644,"S",120,644,"ndroid.systemui",1664
 6824712163783000,0,718000,6824712164501000,0,"R",120,"[NULL]","swapper/5",0
-6824712163811000,1,405000,6824712164216000,43,"S",120,35,"Binder:640_2",675
+6824712163811000,1,405000,6824712164216000,770,"S",120,493,"Binder:640_2",675
 6824712164216000,1,792000,6824712165008000,0,"R",120,"[NULL]","swapper/5",0
-6824712164501000,0,203000,6824712164704000,41,"S",97,35,"app",678
+6824712164501000,0,203000,6824712164704000,773,"S",97,493,"app",678
 6824712164704000,0,26926000,6824712191630000,0,"R",120,"[NULL]","swapper/5",0
-6824712165008000,1,88000,6824712165096000,40,"S",97,35,"DispSync",676
+6824712165008000,1,88000,6824712165096000,771,"S",97,493,"DispSync",676
 6824712165096000,1,26545000,6824712191641000,0,"R",120,"[NULL]","swapper/5",0
-6824712191630000,0,479000,6824712192109000,40,"S",97,35,"DispSync",676
-6824712191641000,1,258000,6824712191899000,47,"S",120,40,"ksoftirqd/1",17
+6824712191630000,0,479000,6824712192109000,771,"S",97,493,"DispSync",676
+6824712191641000,1,258000,6824712191899000,15,"S",120,15,"ksoftirqd/1",17
 6824712191899000,1,160000,6824712192059000,0,"R",120,"[NULL]","swapper/5",0
-6824712192059000,1,615000,6824712192674000,41,"S",97,35,"app",678
-6824712192109000,0,139000,6824712192248000,35,"S",120,33,"kworker/0:5",20371
-6824712192248000,0,299000,6824712192547000,16,"S",120,14,"kworker/u16:7",19422
-6824712192547000,0,966000,6824712193513000,36,"S",111,34,"SDM_EventThread",685
+6824712192059000,1,615000,6824712192674000,773,"S",97,493,"app",678
+6824712192109000,0,139000,6824712192248000,743,"S",120,743,"kworker/0:5",20371
+6824712192248000,0,299000,6824712192547000,702,"S",120,702,"kworker/u16:7",19422
+6824712192547000,0,966000,6824712193513000,786,"S",111,494,"SDM_EventThread",685
 6824712192674000,1,1433000,6824712194107000,0,"R",120,"[NULL]","swapper/5",0
-6824712192943000,2,3173000,6824712196116000,42,"S",120,36,"ndroid.systemui",1664
-6824712193087000,3,109000,6824712193196000,40,"S",97,35,"DispSync",676
+6824712192943000,2,3173000,6824712196116000,644,"S",120,644,"ndroid.systemui",1664
+6824712193087000,3,109000,6824712193196000,771,"S",97,493,"DispSync",676
 6824712193196000,3,33652000,6824712226848000,0,"R",120,"[NULL]","swapper/5",0
 6824712193513000,0,1329000,6824712194842000,0,"R",120,"[NULL]","swapper/5",0
-6824712194107000,1,556000,6824712194663000,38,"S",120,35,"HwBinder:640_1",721
-6824712194398000,7,72000,6824712194470000,17,"S",120,16,"kworker/u16:2",19725
+6824712194107000,1,556000,6824712194663000,777,"S",120,493,"HwBinder:640_1",721
+6824712194398000,7,72000,6824712194470000,711,"S",120,711,"kworker/u16:2",19725
 6824712194470000,7,52792000,6824712247262000,0,"R",120,"[NULL]","swapper/5",0
 6824712194663000,1,2149000,6824712196812000,0,"R",120,"[NULL]","swapper/5",0
-6824712194842000,0,89000,6824712194931000,40,"S",97,35,"DispSync",676
+6824712194842000,0,89000,6824712194931000,771,"S",97,493,"DispSync",676
 6824712194931000,0,1217000,6824712196148000,0,"R",120,"[NULL]","swapper/5",0
 6824712196116000,2,30414000,6824712226530000,0,"R",120,"[NULL]","swapper/5",0
-6824712196148000,0,385000,6824712196533000,43,"S",120,35,"Binder:640_2",675
+6824712196148000,0,385000,6824712196533000,770,"S",120,493,"Binder:640_2",675
 6824712196533000,0,781000,6824712197314000,0,"R",120,"[NULL]","swapper/5",0
-6824712196812000,1,200000,6824712197012000,41,"S",97,35,"app",678
+6824712196812000,1,200000,6824712197012000,773,"S",97,493,"app",678
 6824712197012000,1,28707000,6824712225719000,0,"R",120,"[NULL]","swapper/5",0
-6824712197314000,0,88000,6824712197402000,40,"S",97,35,"DispSync",676
+6824712197314000,0,88000,6824712197402000,771,"S",97,493,"DispSync",676
 6824712197402000,0,28073000,6824712225475000,0,"R",120,"[NULL]","swapper/5",0
-6824712225475000,0,204000,6824712225679000,35,"S",120,33,"kworker/0:5",20371
-6824712225679000,0,916000,6824712226595000,36,"S",111,34,"SDM_EventThread",685
-6824712225719000,1,521000,6824712226240000,40,"S",97,35,"DispSync",676
+6824712225475000,0,204000,6824712225679000,743,"S",120,743,"kworker/0:5",20371
+6824712225679000,0,916000,6824712226595000,786,"S",111,494,"SDM_EventThread",685
+6824712225719000,1,521000,6824712226240000,771,"S",97,493,"DispSync",676
 6824712226240000,1,81000,6824712226321000,0,"R",120,"[NULL]","swapper/5",0
-6824712226321000,1,87000,6824712226408000,47,"S",120,40,"ksoftirqd/1",17
+6824712226321000,1,87000,6824712226408000,15,"S",120,15,"ksoftirqd/1",17
 6824712226408000,1,1183000,6824712227591000,0,"R",120,"[NULL]","swapper/5",0
-6824712226530000,2,669000,6824712227199000,41,"S",97,35,"app",678
+6824712226530000,2,669000,6824712227199000,773,"S",97,493,"app",678
 6824712226595000,0,894000,6824712227489000,0,"R",120,"[NULL]","swapper/5",0
-6824712226848000,3,546000,6824712227394000,38,"S",120,35,"HwBinder:640_1",721
+6824712226848000,3,546000,6824712227394000,777,"S",120,493,"HwBinder:640_1",721
 6824712227199000,2,32486000,6824712259685000,0,"R",120,"[NULL]","swapper/5",0
 6824712227394000,3,101387000,6824712328781000,0,"R",120,"[NULL]","swapper/5",0
-6824712227489000,0,3161000,6824712230650000,42,"S",120,36,"ndroid.systemui",1664
-6824712227591000,1,88000,6824712227679000,40,"S",97,35,"DispSync",676
+6824712227489000,0,3161000,6824712230650000,644,"S",120,644,"ndroid.systemui",1664
+6824712227591000,1,88000,6824712227679000,771,"S",97,493,"DispSync",676
 6824712227679000,1,3014000,6824712230693000,0,"R",120,"[NULL]","swapper/5",0
 6824712230650000,0,717000,6824712231367000,0,"R",120,"[NULL]","swapper/5",0
-6824712230693000,1,394000,6824712231087000,43,"S",120,35,"Binder:640_2",675
+6824712230693000,1,394000,6824712231087000,770,"S",120,493,"Binder:640_2",675
 6824712231087000,1,782000,6824712231869000,0,"R",120,"[NULL]","swapper/5",0
-6824712231367000,0,200000,6824712231567000,41,"S",97,35,"app",678
+6824712231367000,0,200000,6824712231567000,773,"S",97,493,"app",678
 6824712231567000,0,13149000,6824712244716000,0,"R",120,"[NULL]","swapper/5",0
-6824712231869000,1,87000,6824712231956000,40,"S",97,35,"DispSync",676
+6824712231869000,1,87000,6824712231956000,771,"S",97,493,"DispSync",676
 6824712231956000,1,12891000,6824712244847000,0,"R",120,"[NULL]","swapper/5",0
-6824712244716000,0,324000,6824712245040000,56,"S",120,46,"ogle.android.as",15167
-6824712244847000,1,193000,6824712245040000,57,"S",120,46,"ogle.android.as",15166
+6824712244716000,0,324000,6824712245040000,1808,"S",120,663,"ogle.android.as",15167
+6824712244847000,1,193000,6824712245040000,1807,"S",120,663,"ogle.android.as",15166
 6824712245040000,0,87000,6824712245127000,0,"R",120,"[NULL]","swapper/5",0
 6824712245040000,1,14256000,6824712259296000,0,"R",120,"[NULL]","swapper/5",0
-6824712245127000,0,258000,6824712245385000,44,"S",120,37,"ksoftirqd/0",3
+6824712245127000,0,258000,6824712245385000,3,"S",120,3,"ksoftirqd/0",3
 6824712245385000,0,12652000,6824712258037000,0,"R",120,"[NULL]","swapper/5",0
-6824712247262000,7,203000,6824712247465000,17,"S",120,16,"kworker/u16:2",19725
+6824712247262000,7,203000,6824712247465000,711,"S",120,711,"kworker/u16:2",19725
 6824712247465000,7,71000,6824712247536000,0,"R",120,"[NULL]","swapper/5",0
-6824712247536000,7,62000,6824712247598000,59,"S",120,47,"ksoftirqd/7",65
+6824712247536000,7,62000,6824712247598000,57,"S",120,57,"ksoftirqd/7",65
 6824712247598000,7,14402000,6824712262000000,0,"R",120,"[NULL]","swapper/5",0
-6824712258037000,0,225000,6824712258262000,35,"S",120,33,"kworker/0:5",20371
-6824712258262000,0,1133000,6824712259395000,36,"S",111,34,"SDM_EventThread",685
-6824712259296000,1,626000,6824712259922000,38,"S",120,35,"HwBinder:640_1",721
+6824712258037000,0,225000,6824712258262000,743,"S",120,743,"kworker/0:5",20371
+6824712258262000,0,1133000,6824712259395000,786,"S",111,494,"SDM_EventThread",685
+6824712259296000,1,626000,6824712259922000,777,"S",120,493,"HwBinder:640_1",721
 6824712259395000,0,83000,6824712259478000,0,"R",120,"[NULL]","swapper/5",0
-6824712259478000,0,86000,6824712259564000,44,"S",120,37,"ksoftirqd/0",3
+6824712259478000,0,86000,6824712259564000,3,"S",120,3,"ksoftirqd/0",3
 6824712259564000,0,904000,6824712260468000,0,"R",120,"[NULL]","swapper/5",0
-6824712259685000,2,648000,6824712260333000,40,"S",97,35,"DispSync",676
+6824712259685000,2,648000,6824712260333000,771,"S",97,493,"DispSync",676
 6824712259922000,1,1420000,6824712261342000,0,"R",120,"[NULL]","swapper/5",0
-6824712260333000,2,128000,6824712260461000,31,"S",120,29,"kworker/2:0",18823
+6824712260333000,2,128000,6824712260461000,694,"S",120,694,"kworker/2:0",18823
 6824712260461000,2,979000,6824712261440000,0,"R",120,"[NULL]","swapper/5",0
-6824712260468000,0,523000,6824712260991000,41,"S",97,35,"app",678
+6824712260468000,0,523000,6824712260991000,773,"S",97,493,"app",678
 6824712260991000,0,3489000,6824712264480000,0,"R",120,"[NULL]","swapper/5",0
-6824712261342000,1,3074000,6824712264416000,42,"S",120,36,"ndroid.systemui",1664
-6824712261440000,2,98000,6824712261538000,40,"S",97,35,"DispSync",676
+6824712261342000,1,3074000,6824712264416000,644,"S",120,644,"ndroid.systemui",1664
+6824712261440000,2,98000,6824712261538000,771,"S",97,493,"DispSync",676
 6824712261538000,2,31144000,6824712292682000,0,"R",120,"[NULL]","swapper/5",0
-6824712262000000,7,773000,6824712262773000,17,"S",120,16,"kworker/u16:2",19725
+6824712262000000,7,773000,6824712262773000,711,"S",120,711,"kworker/u16:2",19725
 6824712262773000,7,467225000,6824712729998000,0,"R",120,"[NULL]","swapper/5",0
 6824712264416000,1,745000,6824712265161000,0,"R",120,"[NULL]","swapper/5",0
-6824712264480000,0,399000,6824712264879000,43,"S",120,35,"Binder:640_2",675
+6824712264480000,0,399000,6824712264879000,770,"S",120,493,"Binder:640_2",675
 6824712264879000,0,791000,6824712265670000,0,"R",120,"[NULL]","swapper/5",0
-6824712265161000,1,209000,6824712265370000,41,"S",97,35,"app",678
+6824712265161000,1,209000,6824712265370000,773,"S",97,493,"app",678
 6824712265370000,1,26002000,6824712291372000,0,"R",120,"[NULL]","swapper/5",0
-6824712265670000,0,96000,6824712265766000,40,"S",97,35,"DispSync",676
+6824712265670000,0,96000,6824712265766000,771,"S",97,493,"DispSync",676
 6824712265766000,0,19023000,6824712284789000,0,"R",120,"[NULL]","swapper/5",0
-6824712284789000,0,487000,6824712285276000,60,"D",100,48,"thermal-engine",2490
+6824712284789000,0,487000,6824712285276000,1694,"D",100,660,"thermal-engine",2490
 6824712285276000,0,89000,6824712285365000,0,"R",120,"[NULL]","swapper/5",0
-6824712285365000,0,85000,6824712285450000,44,"S",120,37,"ksoftirqd/0",3
+6824712285365000,0,85000,6824712285450000,3,"S",120,3,"ksoftirqd/0",3
 6824712285450000,0,2460000,6824712287910000,0,"R",120,"[NULL]","swapper/5",0
-6824712287910000,0,209000,6824712288119000,35,"R+",120,33,"kworker/0:5",20371
-6824712288119000,0,555000,6824712288674000,60,"S",100,48,"thermal-engine",2490
-6824712288674000,0,111000,6824712288785000,35,"S",120,33,"kworker/0:5",20371
+6824712287910000,0,209000,6824712288119000,743,"R+",120,743,"kworker/0:5",20371
+6824712288119000,0,555000,6824712288674000,1694,"S",100,660,"thermal-engine",2490
+6824712288674000,0,111000,6824712288785000,743,"S",120,743,"kworker/0:5",20371
 6824712288785000,0,83000,6824712288868000,0,"R",120,"[NULL]","swapper/5",0
-6824712288868000,0,79000,6824712288947000,44,"S",120,37,"ksoftirqd/0",3
+6824712288868000,0,79000,6824712288947000,3,"S",120,3,"ksoftirqd/0",3
 6824712288947000,0,1078000,6824712290025000,0,"R",120,"[NULL]","swapper/5",0
-6824712290025000,0,115000,6824712290140000,35,"S",120,33,"kworker/0:5",20371
-6824712290140000,0,986000,6824712291126000,36,"S",111,34,"SDM_EventThread",685
+6824712290025000,0,115000,6824712290140000,743,"S",120,743,"kworker/0:5",20371
+6824712290140000,0,986000,6824712291126000,786,"S",111,494,"SDM_EventThread",685
 6824712291126000,0,55000,6824712291181000,0,"R",120,"[NULL]","swapper/5",0
-6824712291181000,0,48000,6824712291229000,44,"S",120,37,"ksoftirqd/0",3
+6824712291181000,0,48000,6824712291229000,3,"S",120,3,"ksoftirqd/0",3
 6824712291229000,0,733000,6824712291962000,0,"R",120,"[NULL]","swapper/5",0
-6824712291372000,1,758000,6824712292130000,38,"S",120,35,"HwBinder:640_1",721
-6824712291962000,0,316000,6824712292278000,40,"S",97,35,"DispSync",676
+6824712291372000,1,758000,6824712292130000,777,"S",120,493,"HwBinder:640_1",721
+6824712291962000,0,316000,6824712292278000,771,"S",97,493,"DispSync",676
 6824712292130000,1,1482000,6824712293612000,0,"R",120,"[NULL]","swapper/5",0
 6824712292278000,0,1237000,6824712293515000,0,"R",120,"[NULL]","swapper/5",0
-6824712292682000,2,555000,6824712293237000,41,"S",97,35,"app",678
+6824712292682000,2,555000,6824712293237000,773,"S",97,493,"app",678
 6824712293237000,2,4208000,6824712297445000,0,"R",120,"[NULL]","swapper/5",0
-6824712293515000,0,3069000,6824712296584000,42,"S",120,36,"ndroid.systemui",1664
-6824712293612000,1,83000,6824712293695000,40,"S",97,35,"DispSync",676
+6824712293515000,0,3069000,6824712296584000,644,"S",120,644,"ndroid.systemui",1664
+6824712293612000,1,83000,6824712293695000,771,"S",97,493,"DispSync",676
 6824712293695000,1,2925000,6824712296620000,0,"R",120,"[NULL]","swapper/5",0
 6824712296584000,0,315000,6824712296899000,0,"R",120,"[NULL]","swapper/5",0
-6824712296620000,1,373000,6824712296993000,43,"S",120,35,"Binder:640_2",675
-6824712296899000,0,235000,6824712297134000,41,"S",97,35,"app",678
-6824712296993000,1,153000,6824712297146000,17,"S",120,16,"kworker/u16:2",19725
+6824712296620000,1,373000,6824712296993000,770,"S",120,493,"Binder:640_2",675
+6824712296899000,0,235000,6824712297134000,773,"S",97,493,"app",678
+6824712296993000,1,153000,6824712297146000,711,"S",120,711,"kworker/u16:2",19725
 6824712297134000,0,29796000,6824712326930000,0,"R",120,"[NULL]","swapper/5",0
 6824712297146000,1,30662000,6824712327808000,0,"R",120,"[NULL]","swapper/5",0
-6824712297445000,2,91000,6824712297536000,40,"S",97,35,"DispSync",676
+6824712297445000,2,91000,6824712297536000,771,"S",97,493,"DispSync",676
 6824712297536000,2,29403000,6824712326939000,0,"R",120,"[NULL]","swapper/5",0
-6824712326930000,0,479000,6824712327409000,40,"S",97,35,"DispSync",676
-6824712326939000,2,165000,6824712327104000,25,"S",120,24,"ksoftirqd/2",25
+6824712326930000,0,479000,6824712327409000,771,"S",97,493,"DispSync",676
+6824712326939000,2,165000,6824712327104000,22,"S",120,22,"ksoftirqd/2",25
 6824712327104000,2,1506000,6824712328610000,0,"R",120,"[NULL]","swapper/5",0
-6824712327409000,0,171000,6824712327580000,35,"S",120,33,"kworker/0:5",20371
-6824712327580000,0,701000,6824712328281000,36,"R+",111,34,"SDM_EventThread",685
-6824712327808000,1,528000,6824712328336000,41,"S",97,35,"app",678
-6824712328281000,0,52000,6824712328333000,40,"S",97,35,"DispSync",676
-6824712328333000,0,226000,6824712328559000,36,"S",111,34,"SDM_EventThread",685
+6824712327409000,0,171000,6824712327580000,743,"S",120,743,"kworker/0:5",20371
+6824712327580000,0,701000,6824712328281000,786,"R+",111,494,"SDM_EventThread",685
+6824712327808000,1,528000,6824712328336000,773,"S",97,493,"app",678
+6824712328281000,0,52000,6824712328333000,771,"S",97,493,"DispSync",676
+6824712328333000,0,226000,6824712328559000,786,"S",111,494,"SDM_EventThread",685
 6824712328336000,1,4173000,6824712332509000,0,"R",120,"[NULL]","swapper/5",0
 6824712328559000,0,938000,6824712329497000,0,"R",120,"[NULL]","swapper/5",0
-6824712328610000,2,3183000,6824712331793000,42,"S",120,36,"ndroid.systemui",1664
-6824712328781000,3,574000,6824712329355000,38,"S",120,35,"HwBinder:640_1",721
+6824712328610000,2,3183000,6824712331793000,644,"S",120,644,"ndroid.systemui",1664
+6824712328781000,3,574000,6824712329355000,777,"S",120,493,"HwBinder:640_1",721
 6824712329355000,3,32279000,6824712361634000,0,"R",120,"[NULL]","swapper/5",0
-6824712329497000,0,87000,6824712329584000,40,"S",97,35,"DispSync",676
+6824712329497000,0,87000,6824712329584000,771,"S",97,493,"DispSync",676
 6824712329584000,0,2242000,6824712331826000,0,"R",120,"[NULL]","swapper/5",0
 6824712331793000,2,29260000,6824712361053000,0,"R",120,"[NULL]","swapper/5",0
-6824712331826000,0,394000,6824712332220000,43,"S",120,35,"Binder:640_2",675
+6824712331826000,0,394000,6824712332220000,770,"S",120,493,"Binder:640_2",675
 6824712332220000,0,790000,6824712333010000,0,"R",120,"[NULL]","swapper/5",0
-6824712332509000,1,202000,6824712332711000,41,"S",97,35,"app",678
+6824712332509000,1,202000,6824712332711000,773,"S",97,493,"app",678
 6824712332711000,1,27609000,6824712360320000,0,"R",120,"[NULL]","swapper/5",0
-6824712333010000,0,85000,6824712333095000,40,"S",97,35,"DispSync",676
+6824712333010000,0,85000,6824712333095000,771,"S",97,493,"DispSync",676
 6824712333095000,0,26074000,6824712359169000,0,"R",120,"[NULL]","swapper/5",0
-6824712359169000,0,225000,6824712359394000,35,"S",120,33,"kworker/0:5",20371
-6824712359394000,0,1615000,6824712361009000,36,"S",111,34,"SDM_EventThread",685
-6824712360320000,1,440000,6824712360760000,40,"S",97,35,"DispSync",676
+6824712359169000,0,225000,6824712359394000,743,"S",120,743,"kworker/0:5",20371
+6824712359394000,0,1615000,6824712361009000,786,"S",111,494,"SDM_EventThread",685
+6824712360320000,1,440000,6824712360760000,771,"S",97,493,"DispSync",676
 6824712360760000,1,80000,6824712360840000,0,"R",120,"[NULL]","swapper/5",0
-6824712360840000,1,552000,6824712361392000,38,"S",120,35,"HwBinder:640_1",721
-6824712361009000,0,85000,6824712361094000,35,"S",120,33,"kworker/0:5",20371
-6824712361053000,2,490000,6824712361543000,41,"S",97,35,"app",678
-6824712361094000,0,1024000,6824712362118000,17,"S",120,16,"kworker/u16:2",19725
+6824712360840000,1,552000,6824712361392000,777,"S",120,493,"HwBinder:640_1",721
+6824712361009000,0,85000,6824712361094000,743,"S",120,743,"kworker/0:5",20371
+6824712361053000,2,490000,6824712361543000,773,"S",97,493,"app",678
+6824712361094000,0,1024000,6824712362118000,711,"S",120,711,"kworker/u16:2",19725
 6824712361392000,1,52000,6824712361444000,0,"R",120,"[NULL]","swapper/5",0
-6824712361444000,1,3028000,6824712364472000,42,"S",120,36,"ndroid.systemui",1664
+6824712361444000,1,3028000,6824712364472000,644,"S",120,644,"ndroid.systemui",1664
 6824712361543000,2,32596000,6824712394139000,0,"R",120,"[NULL]","swapper/5",0
-6824712361634000,3,102000,6824712361736000,40,"S",97,35,"DispSync",676
+6824712361634000,3,102000,6824712361736000,771,"S",97,493,"DispSync",676
 6824712361736000,3,33366000,6824712395102000,0,"R",120,"[NULL]","swapper/5",0
 6824712362118000,0,2398000,6824712364516000,0,"R",120,"[NULL]","swapper/5",0
 6824712364472000,1,724000,6824712365196000,0,"R",120,"[NULL]","swapper/5",0
-6824712364516000,0,394000,6824712364910000,43,"S",120,35,"Binder:640_2",675
+6824712364516000,0,394000,6824712364910000,770,"S",120,493,"Binder:640_2",675
 6824712364910000,0,797000,6824712365707000,0,"R",120,"[NULL]","swapper/5",0
-6824712365196000,1,209000,6824712365405000,41,"S",97,35,"app",678
+6824712365196000,1,209000,6824712365405000,773,"S",97,493,"app",678
 6824712365405000,1,27749000,6824712393154000,0,"R",120,"[NULL]","swapper/5",0
-6824712365707000,0,88000,6824712365795000,40,"S",97,35,"DispSync",676
+6824712365707000,0,88000,6824712365795000,771,"S",97,493,"DispSync",676
 6824712365795000,0,27098000,6824712392893000,0,"R",120,"[NULL]","swapper/5",0
-6824712392893000,0,266000,6824712393159000,35,"S",120,33,"kworker/0:5",20371
-6824712393154000,1,677000,6824712393831000,40,"S",97,35,"DispSync",676
-6824712393159000,0,1045000,6824712394204000,36,"S",111,34,"SDM_EventThread",685
+6824712392893000,0,266000,6824712393159000,743,"S",120,743,"kworker/0:5",20371
+6824712393154000,1,677000,6824712393831000,771,"S",97,493,"DispSync",676
+6824712393159000,0,1045000,6824712394204000,786,"S",111,494,"SDM_EventThread",685
 6824712393831000,1,936000,6824712394767000,0,"R",120,"[NULL]","swapper/5",0
-6824712394139000,2,561000,6824712394700000,41,"S",97,35,"app",678
+6824712394139000,2,561000,6824712394700000,773,"S",97,493,"app",678
 6824712394204000,0,745000,6824712394949000,0,"R",120,"[NULL]","swapper/5",0
 6824712394700000,2,32976000,6824712427676000,0,"R",120,"[NULL]","swapper/5",0
-6824712394767000,1,525000,6824712395292000,38,"S",120,35,"HwBinder:640_1",721
-6824712394949000,0,3133000,6824712398082000,42,"S",120,36,"ndroid.systemui",1664
-6824712395102000,3,102000,6824712395204000,40,"S",97,35,"DispSync",676
+6824712394767000,1,525000,6824712395292000,777,"S",120,493,"HwBinder:640_1",721
+6824712394949000,0,3133000,6824712398082000,644,"S",120,644,"ndroid.systemui",1664
+6824712395102000,3,102000,6824712395204000,771,"S",97,493,"DispSync",676
 6824712395204000,3,33047000,6824712428251000,0,"R",120,"[NULL]","swapper/5",0
 6824712395292000,1,2827000,6824712398119000,0,"R",120,"[NULL]","swapper/5",0
 6824712398082000,0,723000,6824712398805000,0,"R",120,"[NULL]","swapper/5",0
-6824712398119000,1,404000,6824712398523000,43,"S",120,35,"Binder:640_2",675
+6824712398119000,1,404000,6824712398523000,770,"S",120,493,"Binder:640_2",675
 6824712398523000,1,782000,6824712399305000,0,"R",120,"[NULL]","swapper/5",0
-6824712398805000,0,206000,6824712399011000,41,"S",97,35,"app",678
+6824712398805000,0,206000,6824712399011000,773,"S",97,493,"app",678
 6824712399011000,0,27138000,6824712426149000,0,"R",120,"[NULL]","swapper/5",0
-6824712399305000,1,88000,6824712399393000,40,"S",97,35,"DispSync",676
+6824712399305000,1,88000,6824712399393000,771,"S",97,493,"DispSync",676
 6824712399393000,1,27547000,6824712426940000,0,"R",120,"[NULL]","swapper/5",0
-6824712426149000,0,223000,6824712426372000,35,"S",120,33,"kworker/0:5",20371
-6824712426372000,0,1235000,6824712427607000,36,"S",111,34,"SDM_EventThread",685
-6824712426940000,1,447000,6824712427387000,40,"S",97,35,"DispSync",676
+6824712426149000,0,223000,6824712426372000,743,"S",120,743,"kworker/0:5",20371
+6824712426372000,0,1235000,6824712427607000,786,"S",111,494,"SDM_EventThread",685
+6824712426940000,1,447000,6824712427387000,771,"S",97,493,"DispSync",676
 6824712427387000,1,62000,6824712427449000,0,"R",120,"[NULL]","swapper/5",0
-6824712427449000,1,556000,6824712428005000,38,"S",120,35,"HwBinder:640_1",721
-6824712427607000,0,142000,6824712427749000,17,"S",120,16,"kworker/u16:2",19725
-6824712427676000,2,485000,6824712428161000,41,"S",97,35,"app",678
+6824712427449000,1,556000,6824712428005000,777,"S",120,493,"HwBinder:640_1",721
+6824712427607000,0,142000,6824712427749000,711,"S",120,711,"kworker/u16:2",19725
+6824712427676000,2,485000,6824712428161000,773,"S",97,493,"app",678
 6824712427749000,0,1055000,6824712428804000,0,"R",120,"[NULL]","swapper/5",0
 6824712428005000,1,3964000,6824712431969000,0,"R",120,"[NULL]","swapper/5",0
 6824712428161000,2,33130000,6824712461291000,0,"R",120,"[NULL]","swapper/5",0
-6824712428251000,3,103000,6824712428354000,40,"S",97,35,"DispSync",676
+6824712428251000,3,103000,6824712428354000,771,"S",97,493,"DispSync",676
 6824712428354000,3,33090000,6824712461444000,0,"R",120,"[NULL]","swapper/5",0
-6824712428804000,0,3132000,6824712431936000,42,"S",120,36,"ndroid.systemui",1664
+6824712428804000,0,3132000,6824712431936000,644,"S",120,644,"ndroid.systemui",1664
 6824712431936000,0,704000,6824712432640000,0,"R",120,"[NULL]","swapper/5",0
-6824712431969000,1,397000,6824712432366000,43,"S",120,35,"Binder:640_2",675
+6824712431969000,1,397000,6824712432366000,770,"S",120,493,"Binder:640_2",675
 6824712432366000,1,792000,6824712433158000,0,"R",120,"[NULL]","swapper/5",0
-6824712432640000,0,216000,6824712432856000,41,"S",97,35,"app",678
+6824712432640000,0,216000,6824712432856000,773,"S",97,493,"app",678
 6824712432856000,0,26763000,6824712459619000,0,"R",120,"[NULL]","swapper/5",0
-6824712433158000,1,88000,6824712433246000,40,"S",97,35,"DispSync",676
+6824712433158000,1,88000,6824712433246000,771,"S",97,493,"DispSync",676
 6824712433246000,1,27279000,6824712460525000,0,"R",120,"[NULL]","swapper/5",0
-6824712459619000,0,225000,6824712459844000,35,"S",120,33,"kworker/0:5",20371
-6824712459844000,0,1326000,6824712461170000,36,"S",111,34,"SDM_EventThread",685
-6824712460525000,1,447000,6824712460972000,40,"S",97,35,"DispSync",676
+6824712459619000,0,225000,6824712459844000,743,"S",120,743,"kworker/0:5",20371
+6824712459844000,0,1326000,6824712461170000,786,"S",111,494,"SDM_EventThread",685
+6824712460525000,1,447000,6824712460972000,771,"S",97,493,"DispSync",676
 6824712460972000,1,1100000,6824712462072000,0,"R",120,"[NULL]","swapper/5",0
-6824712461170000,0,668000,6824712461838000,17,"R+",120,16,"kworker/u16:2",19725
-6824712461291000,2,529000,6824712461820000,41,"S",97,35,"app",678
-6824712461444000,3,570000,6824712462014000,38,"S",120,35,"HwBinder:640_1",721
+6824712461170000,0,668000,6824712461838000,711,"R+",120,711,"kworker/u16:2",19725
+6824712461291000,2,529000,6824712461820000,773,"S",97,493,"app",678
+6824712461444000,3,570000,6824712462014000,777,"S",120,493,"HwBinder:640_1",721
 6824712461820000,2,32964000,6824712494784000,0,"R",120,"[NULL]","swapper/5",0
-6824712461838000,0,60000,6824712461898000,40,"S",97,35,"DispSync",676
-6824712461898000,0,133000,6824712462031000,35,"S",120,33,"kworker/0:5",20371
+6824712461838000,0,60000,6824712461898000,771,"S",97,493,"DispSync",676
+6824712461898000,0,133000,6824712462031000,743,"S",120,743,"kworker/0:5",20371
 6824712462014000,3,33363000,6824712495377000,0,"R",120,"[NULL]","swapper/5",0
-6824712462031000,0,281000,6824712462312000,17,"S",120,16,"kworker/u16:2",19725
-6824712462072000,1,3236000,6824712465308000,42,"S",120,36,"ndroid.systemui",1664
+6824712462031000,0,281000,6824712462312000,711,"S",120,711,"kworker/u16:2",19725
+6824712462072000,1,3236000,6824712465308000,644,"S",120,644,"ndroid.systemui",1664
 6824712462312000,0,3049000,6824712465361000,0,"R",120,"[NULL]","swapper/5",0
 6824712465308000,1,737000,6824712466045000,0,"R",120,"[NULL]","swapper/5",0
-6824712465361000,0,404000,6824712465765000,43,"S",120,35,"Binder:640_2",675
+6824712465361000,0,404000,6824712465765000,770,"S",120,493,"Binder:640_2",675
 6824712465765000,0,787000,6824712466552000,0,"R",120,"[NULL]","swapper/5",0
-6824712466045000,1,204000,6824712466249000,41,"S",97,35,"app",678
+6824712466045000,1,204000,6824712466249000,773,"S",97,493,"app",678
 6824712466249000,1,27754000,6824712494003000,0,"R",120,"[NULL]","swapper/5",0
-6824712466552000,0,174000,6824712466726000,40,"S",97,35,"DispSync",676
+6824712466552000,0,174000,6824712466726000,771,"S",97,493,"DispSync",676
 6824712466726000,0,26386000,6824712493112000,0,"R",120,"[NULL]","swapper/5",0
-6824712493112000,0,746000,6824712493858000,35,"S",120,33,"kworker/0:5",20371
-6824712493858000,0,917000,6824712494775000,36,"S",111,34,"SDM_EventThread",685
-6824712494003000,1,505000,6824712494508000,40,"S",97,35,"DispSync",676
+6824712493112000,0,746000,6824712493858000,743,"S",120,743,"kworker/0:5",20371
+6824712493858000,0,917000,6824712494775000,786,"S",111,494,"SDM_EventThread",685
+6824712494003000,1,505000,6824712494508000,771,"S",97,493,"DispSync",676
 6824712494508000,1,79000,6824712494587000,0,"R",120,"[NULL]","swapper/5",0
-6824712494587000,1,558000,6824712495145000,38,"S",120,35,"HwBinder:640_1",721
-6824712494775000,0,149000,6824712494924000,17,"S",120,16,"kworker/u16:2",19725
-6824712494784000,2,486000,6824712495270000,41,"S",97,35,"app",678
+6824712494587000,1,558000,6824712495145000,777,"S",120,493,"HwBinder:640_1",721
+6824712494775000,0,149000,6824712494924000,711,"S",120,711,"kworker/u16:2",19725
+6824712494784000,2,486000,6824712495270000,773,"S",97,493,"app",678
 6824712494924000,0,989000,6824712495913000,0,"R",120,"[NULL]","swapper/5",0
 6824712495145000,1,3951000,6824712499096000,0,"R",120,"[NULL]","swapper/5",0
 6824712495270000,2,33176000,6824712528446000,0,"R",120,"[NULL]","swapper/5",0
-6824712495377000,3,105000,6824712495482000,40,"S",97,35,"DispSync",676
+6824712495377000,3,105000,6824712495482000,771,"S",97,493,"DispSync",676
 6824712495482000,3,33062000,6824712528544000,0,"R",120,"[NULL]","swapper/5",0
-6824712495913000,0,3157000,6824712499070000,42,"S",120,36,"ndroid.systemui",1664
+6824712495913000,0,3157000,6824712499070000,644,"S",120,644,"ndroid.systemui",1664
 6824712499070000,0,712000,6824712499782000,0,"R",120,"[NULL]","swapper/5",0
-6824712499096000,1,408000,6824712499504000,43,"S",120,35,"Binder:640_2",675
+6824712499096000,1,408000,6824712499504000,770,"S",120,493,"Binder:640_2",675
 6824712499504000,1,790000,6824712500294000,0,"R",120,"[NULL]","swapper/5",0
-6824712499782000,0,306000,6824712500088000,41,"S",97,35,"app",678
+6824712499782000,0,306000,6824712500088000,773,"S",97,493,"app",678
 6824712500088000,0,26796000,6824712526884000,0,"R",120,"[NULL]","swapper/5",0
-6824712500294000,1,92000,6824712500386000,40,"S",97,35,"DispSync",676
+6824712500294000,1,92000,6824712500386000,771,"S",97,493,"DispSync",676
 6824712500386000,1,27266000,6824712527652000,0,"R",120,"[NULL]","swapper/5",0
-6824712526884000,0,181000,6824712527065000,35,"S",120,33,"kworker/0:5",20371
-6824712527065000,0,125000,6824712527190000,17,"S",120,16,"kworker/u16:2",19725
-6824712527190000,0,978000,6824712528168000,36,"S",111,34,"SDM_EventThread",685
-6824712527652000,1,437000,6824712528089000,40,"S",97,35,"DispSync",676
+6824712526884000,0,181000,6824712527065000,743,"S",120,743,"kworker/0:5",20371
+6824712527065000,0,125000,6824712527190000,711,"S",120,711,"kworker/u16:2",19725
+6824712527190000,0,978000,6824712528168000,786,"S",111,494,"SDM_EventThread",685
+6824712527652000,1,437000,6824712528089000,771,"S",97,493,"DispSync",676
 6824712528089000,1,1245000,6824712529334000,0,"R",120,"[NULL]","swapper/5",0
-6824712528168000,0,70000,6824712528238000,16,"S",120,14,"kworker/u16:7",19422
+6824712528168000,0,70000,6824712528238000,702,"S",120,702,"kworker/u16:7",19422
 6824712528238000,0,994000,6824712529232000,0,"R",120,"[NULL]","swapper/5",0
-6824712528446000,2,487000,6824712528933000,41,"S",97,35,"app",678
-6824712528544000,3,620000,6824712529164000,38,"S",120,35,"HwBinder:640_1",721
+6824712528446000,2,487000,6824712528933000,773,"S",97,493,"app",678
+6824712528544000,3,620000,6824712529164000,777,"S",120,493,"HwBinder:640_1",721
 6824712528933000,2,52000,6824712528985000,0,"R",120,"[NULL]","swapper/5",0
-6824712528985000,2,78000,6824712529063000,41,"S",97,35,"app",678
+6824712528985000,2,78000,6824712529063000,773,"S",97,493,"app",678
 6824712529063000,2,32595000,6824712561658000,0,"R",120,"[NULL]","swapper/5",0
 6824712529164000,3,33240000,6824712562404000,0,"R",120,"[NULL]","swapper/5",0
-6824712529232000,0,835000,6824712530067000,42,"R",120,36,"ndroid.systemui",1664
-6824712529334000,1,86000,6824712529420000,40,"S",97,35,"DispSync",676
+6824712529232000,0,835000,6824712530067000,644,"R",120,644,"ndroid.systemui",1664
+6824712529334000,1,86000,6824712529420000,771,"S",97,493,"DispSync",676
 6824712529420000,1,3171000,6824712532591000,0,"R",120,"[NULL]","swapper/5",0
-6824712530067000,0,157000,6824712530224000,62,"S",120,43,"Executor-7",14763
-6824712530224000,0,2349000,6824712532573000,42,"S",120,36,"ndroid.systemui",1664
+6824712530067000,0,157000,6824712530224000,1920,"S",120,667,"Executor-7",14763
+6824712530224000,0,2349000,6824712532573000,644,"S",120,644,"ndroid.systemui",1664
 6824712532573000,0,701000,6824712533274000,0,"R",120,"[NULL]","swapper/5",0
-6824712532591000,1,395000,6824712532986000,43,"S",120,35,"Binder:640_2",675
+6824712532591000,1,395000,6824712532986000,770,"S",120,493,"Binder:640_2",675
 6824712532986000,1,786000,6824712533772000,0,"R",120,"[NULL]","swapper/5",0
-6824712533274000,0,198000,6824712533472000,41,"S",97,35,"app",678
+6824712533274000,0,198000,6824712533472000,773,"S",97,493,"app",678
 6824712533472000,0,70000,6824712533542000,0,"R",120,"[NULL]","swapper/5",0
-6824712533542000,0,67000,6824712533609000,44,"S",120,37,"ksoftirqd/0",3
+6824712533542000,0,67000,6824712533609000,3,"S",120,3,"ksoftirqd/0",3
 6824712533609000,0,26476000,6824712560085000,0,"R",120,"[NULL]","swapper/5",0
-6824712533772000,1,89000,6824712533861000,40,"S",97,35,"DispSync",676
+6824712533772000,1,89000,6824712533861000,771,"S",97,493,"DispSync",676
 6824712533861000,1,27413000,6824712561274000,0,"R",120,"[NULL]","swapper/5",0
-6824712560085000,0,224000,6824712560309000,35,"S",120,33,"kworker/0:5",20371
-6824712560309000,0,1129000,6824712561438000,36,"S",111,34,"SDM_EventThread",685
-6824712561274000,1,622000,6824712561896000,38,"S",120,35,"HwBinder:640_1",721
+6824712560085000,0,224000,6824712560309000,743,"S",120,743,"kworker/0:5",20371
+6824712560309000,0,1129000,6824712561438000,786,"S",111,494,"SDM_EventThread",685
+6824712561274000,1,622000,6824712561896000,777,"S",120,493,"HwBinder:640_1",721
 6824712561438000,0,79000,6824712561517000,0,"R",120,"[NULL]","swapper/5",0
-6824712561517000,0,189000,6824712561706000,44,"S",120,37,"ksoftirqd/0",3
-6824712561658000,2,329000,6824712561987000,40,"S",97,35,"DispSync",676
-6824712561706000,0,1079000,6824712562785000,16,"S",120,14,"kworker/u16:7",19422
+6824712561517000,0,189000,6824712561706000,3,"S",120,3,"ksoftirqd/0",3
+6824712561658000,2,329000,6824712561987000,771,"S",97,493,"DispSync",676
+6824712561706000,0,1079000,6824712562785000,702,"S",120,702,"kworker/u16:7",19422
 6824712561896000,1,1355000,6824712563251000,0,"R",120,"[NULL]","swapper/5",0
 6824712561987000,2,1400000,6824712563387000,0,"R",120,"[NULL]","swapper/5",0
-6824712562404000,3,576000,6824712562980000,41,"S",97,35,"app",678
-6824712562785000,0,106000,6824712562891000,35,"S",120,33,"kworker/0:5",20371
+6824712562404000,3,576000,6824712562980000,773,"S",97,493,"app",678
+6824712562785000,0,106000,6824712562891000,743,"S",120,743,"kworker/0:5",20371
 6824712562891000,0,3455000,6824712566346000,0,"R",120,"[NULL]","swapper/5",0
 6824712562980000,3,66067000,6824712629047000,0,"R",120,"[NULL]","swapper/5",0
-6824712563251000,1,3044000,6824712566295000,42,"S",120,36,"ndroid.systemui",1664
-6824712563387000,2,84000,6824712563471000,40,"S",97,35,"DispSync",676
+6824712563251000,1,3044000,6824712566295000,644,"S",120,644,"ndroid.systemui",1664
+6824712563387000,2,84000,6824712563471000,771,"S",97,493,"DispSync",676
 6824712563471000,2,66000,6824712563537000,0,"R",120,"[NULL]","swapper/5",0
-6824712563537000,2,61000,6824712563598000,25,"S",120,24,"ksoftirqd/2",25
+6824712563537000,2,61000,6824712563598000,22,"S",120,22,"ksoftirqd/2",25
 6824712563598000,2,28176000,6824712591774000,0,"R",120,"[NULL]","swapper/5",0
 6824712566295000,1,912000,6824712567207000,0,"R",120,"[NULL]","swapper/5",0
-6824712566346000,0,580000,6824712566926000,43,"S",120,35,"Binder:640_2",675
+6824712566346000,0,580000,6824712566926000,770,"S",120,493,"Binder:640_2",675
 6824712566926000,0,798000,6824712567724000,0,"R",120,"[NULL]","swapper/5",0
-6824712567207000,1,209000,6824712567416000,41,"S",97,35,"app",678
+6824712567207000,1,209000,6824712567416000,773,"S",97,493,"app",678
 6824712567416000,1,25855000,6824712593271000,0,"R",120,"[NULL]","swapper/5",0
-6824712567724000,0,90000,6824712567814000,40,"S",97,35,"DispSync",676
+6824712567724000,0,90000,6824712567814000,771,"S",97,493,"DispSync",676
 6824712567814000,0,23951000,6824712591765000,0,"R",120,"[NULL]","swapper/5",0
-6824712591765000,0,69000,6824712591834000,44,"S",120,37,"ksoftirqd/0",3
-6824712591774000,2,643000,6824712592417000,63,"S",120,49,"hwservicemanage",602
-6824712591834000,0,141000,6824712591975000,35,"S",120,33,"kworker/0:5",20371
-6824712591975000,0,943000,6824712592918000,36,"S",111,34,"SDM_EventThread",685
+6824712591765000,0,69000,6824712591834000,3,"S",120,3,"ksoftirqd/0",3
+6824712591774000,2,643000,6824712592417000,479,"S",120,479,"hwservicemanage",602
+6824712591834000,0,141000,6824712591975000,743,"S",120,743,"kworker/0:5",20371
+6824712591975000,0,943000,6824712592918000,786,"S",111,494,"SDM_EventThread",685
 6824712592417000,2,1974000,6824712594391000,0,"R",120,"[NULL]","swapper/5",0
 6824712592918000,0,855000,6824712593773000,0,"R",120,"[NULL]","swapper/5",0
-6824712593271000,1,748000,6824712594019000,38,"S",120,35,"HwBinder:640_1",721
-6824712593773000,0,313000,6824712594086000,40,"S",97,35,"DispSync",676
+6824712593271000,1,748000,6824712594019000,777,"S",120,493,"HwBinder:640_1",721
+6824712593773000,0,313000,6824712594086000,771,"S",97,493,"DispSync",676
 6824712594019000,1,75000,6824712594094000,0,"R",120,"[NULL]","swapper/5",0
 6824712594086000,0,1123000,6824712595209000,0,"R",120,"[NULL]","swapper/5",0
-6824712594094000,1,73000,6824712594167000,47,"S",120,40,"ksoftirqd/1",17
+6824712594094000,1,73000,6824712594167000,15,"S",120,15,"ksoftirqd/1",17
 6824712594167000,1,1145000,6824712595312000,0,"R",120,"[NULL]","swapper/5",0
-6824712594391000,2,543000,6824712594934000,41,"S",97,35,"app",678
+6824712594391000,2,543000,6824712594934000,773,"S",97,493,"app",678
 6824712594934000,2,33189000,6824712628123000,0,"R",120,"[NULL]","swapper/5",0
-6824712595209000,0,3193000,6824712598402000,42,"S",120,36,"ndroid.systemui",1664
-6824712595312000,1,84000,6824712595396000,40,"S",97,35,"DispSync",676
+6824712595209000,0,3193000,6824712598402000,644,"S",120,644,"ndroid.systemui",1664
+6824712595312000,1,84000,6824712595396000,771,"S",97,493,"DispSync",676
 6824712595396000,1,3046000,6824712598442000,0,"R",120,"[NULL]","swapper/5",0
 6824712598402000,0,638000,6824712599040000,0,"R",120,"[NULL]","swapper/5",0
-6824712598442000,1,395000,6824712598837000,43,"S",120,35,"Binder:640_2",675
+6824712598442000,1,395000,6824712598837000,770,"S",120,493,"Binder:640_2",675
 6824712598837000,1,566000,6824712599403000,0,"R",120,"[NULL]","swapper/5",0
-6824712599040000,0,182000,6824712599222000,41,"S",97,35,"app",678
+6824712599040000,0,182000,6824712599222000,773,"S",97,493,"app",678
 6824712599222000,0,27826000,6824712627048000,0,"R",120,"[NULL]","swapper/5",0
-6824712599403000,1,87000,6824712599490000,40,"S",97,35,"DispSync",676
+6824712599403000,1,87000,6824712599490000,771,"S",97,493,"DispSync",676
 6824712599490000,1,27821000,6824712627311000,0,"R",120,"[NULL]","swapper/5",0
-6824712627048000,0,215000,6824712627263000,35,"S",120,33,"kworker/0:5",20371
-6824712627263000,0,865000,6824712628128000,36,"S",111,34,"SDM_EventThread",685
-6824712627311000,1,535000,6824712627846000,40,"S",97,35,"DispSync",676
+6824712627048000,0,215000,6824712627263000,743,"S",120,743,"kworker/0:5",20371
+6824712627263000,0,865000,6824712628128000,786,"S",111,494,"SDM_EventThread",685
+6824712627311000,1,535000,6824712627846000,771,"S",97,493,"DispSync",676
 6824712627846000,1,97000,6824712627943000,0,"R",120,"[NULL]","swapper/5",0
-6824712627943000,1,143000,6824712628086000,47,"S",120,40,"ksoftirqd/1",17
-6824712628086000,1,559000,6824712628645000,38,"S",120,35,"HwBinder:640_1",721
-6824712628123000,2,487000,6824712628610000,41,"S",97,35,"app",678
-6824712628128000,0,134000,6824712628262000,16,"S",120,14,"kworker/u16:7",19422
+6824712627943000,1,143000,6824712628086000,15,"S",120,15,"ksoftirqd/1",17
+6824712628086000,1,559000,6824712628645000,777,"S",120,493,"HwBinder:640_1",721
+6824712628123000,2,487000,6824712628610000,773,"S",97,493,"app",678
+6824712628128000,0,134000,6824712628262000,702,"S",120,702,"kworker/u16:7",19422
 6824712628262000,0,1140000,6824712629402000,0,"R",120,"[NULL]","swapper/5",0
 6824712628610000,2,29797000,6824712658407000,0,"R",120,"[NULL]","swapper/5",0
 6824712628645000,1,4458000,6824712633103000,0,"R",120,"[NULL]","swapper/5",0
-6824712629047000,3,3339000,6824712632386000,42,"S",120,36,"ndroid.systemui",1664
-6824712629402000,0,86000,6824712629488000,40,"S",97,35,"DispSync",676
+6824712629047000,3,3339000,6824712632386000,644,"S",120,644,"ndroid.systemui",1664
+6824712629402000,0,86000,6824712629488000,771,"S",97,493,"DispSync",676
 6824712629488000,0,2932000,6824712632420000,0,"R",120,"[NULL]","swapper/5",0
 6824712632386000,3,20618000,6824712653004000,0,"R",120,"[NULL]","swapper/5",0
-6824712632420000,0,405000,6824712632825000,43,"S",120,35,"Binder:640_2",675
+6824712632420000,0,405000,6824712632825000,770,"S",120,493,"Binder:640_2",675
 6824712632825000,0,777000,6824712633602000,0,"R",120,"[NULL]","swapper/5",0
-6824712633103000,1,300000,6824712633403000,41,"S",97,35,"app",678
+6824712633103000,1,300000,6824712633403000,773,"S",97,493,"app",678
 6824712633403000,1,24713000,6824712658116000,0,"R",120,"[NULL]","swapper/5",0
-6824712633602000,0,89000,6824712633691000,40,"S",97,35,"DispSync",676
+6824712633602000,0,89000,6824712633691000,771,"S",97,493,"DispSync",676
 6824712633691000,0,8761000,6824712642452000,0,"R",120,"[NULL]","swapper/5",0
-6824712642452000,0,1867000,6824712644319000,16,"R+",120,14,"kworker/u16:7",19422
-6824712644319000,0,91000,6824712644410000,64,"S",100,50,"kworker/0:1H",558
-6824712644410000,0,777000,6824712645187000,17,"D",120,16,"kworker/u16:2",19725
-6824712645187000,0,70000,6824712645257000,32,"S",120,30,"smem_native_rpm",87
-6824712645257000,0,107000,6824712645364000,16,"S",120,14,"kworker/u16:7",19422
+6824712642452000,0,1867000,6824712644319000,702,"R+",120,702,"kworker/u16:7",19422
+6824712644319000,0,91000,6824712644410000,458,"S",100,458,"kworker/0:1H",558
+6824712644410000,0,777000,6824712645187000,711,"D",120,711,"kworker/u16:2",19725
+6824712645187000,0,70000,6824712645257000,77,"S",120,77,"smem_native_rpm",87
+6824712645257000,0,107000,6824712645364000,702,"S",120,702,"kworker/u16:7",19422
 6824712645364000,0,605000,6824712645969000,0,"R",120,"[NULL]","swapper/5",0
-6824712645969000,0,204000,6824712646173000,17,"D",120,16,"kworker/u16:2",19725
-6824712646173000,0,123000,6824712646296000,32,"S",120,30,"smem_native_rpm",87
-6824712646296000,0,140000,6824712646436000,17,"D",120,16,"kworker/u16:2",19725
-6824712646436000,0,48000,6824712646484000,32,"S",120,30,"smem_native_rpm",87
+6824712645969000,0,204000,6824712646173000,711,"D",120,711,"kworker/u16:2",19725
+6824712646173000,0,123000,6824712646296000,77,"S",120,77,"smem_native_rpm",87
+6824712646296000,0,140000,6824712646436000,711,"D",120,711,"kworker/u16:2",19725
+6824712646436000,0,48000,6824712646484000,77,"S",120,77,"smem_native_rpm",87
 6824712646484000,0,171000,6824712646655000,0,"R",120,"[NULL]","swapper/5",0
-6824712646655000,0,46000,6824712646701000,64,"S",100,50,"kworker/0:1H",558
-6824712646701000,0,144000,6824712646845000,17,"D",120,16,"kworker/u16:2",19725
-6824712646845000,0,124000,6824712646969000,32,"S",120,30,"smem_native_rpm",87
-6824712646969000,0,167000,6824712647136000,17,"D",120,16,"kworker/u16:2",19725
-6824712647136000,0,49000,6824712647185000,32,"S",120,30,"smem_native_rpm",87
+6824712646655000,0,46000,6824712646701000,458,"S",100,458,"kworker/0:1H",558
+6824712646701000,0,144000,6824712646845000,711,"D",120,711,"kworker/u16:2",19725
+6824712646845000,0,124000,6824712646969000,77,"S",120,77,"smem_native_rpm",87
+6824712646969000,0,167000,6824712647136000,711,"D",120,711,"kworker/u16:2",19725
+6824712647136000,0,49000,6824712647185000,77,"S",120,77,"smem_native_rpm",87
 6824712647185000,0,104000,6824712647289000,0,"R",120,"[NULL]","swapper/5",0
-6824712647289000,0,146000,6824712647435000,17,"D",120,16,"kworker/u16:2",19725
-6824712647435000,0,48000,6824712647483000,32,"S",120,30,"smem_native_rpm",87
+6824712647289000,0,146000,6824712647435000,711,"D",120,711,"kworker/u16:2",19725
+6824712647435000,0,48000,6824712647483000,77,"S",120,77,"smem_native_rpm",87
 6824712647483000,0,945000,6824712648428000,0,"R",120,"[NULL]","swapper/5",0
-6824712648428000,0,169000,6824712648597000,17,"D",120,16,"kworker/u16:2",19725
-6824712648597000,0,119000,6824712648716000,32,"S",120,30,"smem_native_rpm",87
-6824712648716000,0,149000,6824712648865000,17,"D",120,16,"kworker/u16:2",19725
-6824712648865000,0,54000,6824712648919000,32,"S",120,30,"smem_native_rpm",87
+6824712648428000,0,169000,6824712648597000,711,"D",120,711,"kworker/u16:2",19725
+6824712648597000,0,119000,6824712648716000,77,"S",120,77,"smem_native_rpm",87
+6824712648716000,0,149000,6824712648865000,711,"D",120,711,"kworker/u16:2",19725
+6824712648865000,0,54000,6824712648919000,77,"S",120,77,"smem_native_rpm",87
 6824712648919000,0,62000,6824712648981000,0,"R",120,"[NULL]","swapper/5",0
-6824712648981000,0,150000,6824712649131000,17,"D",120,16,"kworker/u16:2",19725
-6824712649131000,0,113000,6824712649244000,32,"S",120,30,"smem_native_rpm",87
-6824712649244000,0,139000,6824712649383000,17,"D",120,16,"kworker/u16:2",19725
-6824712649383000,0,48000,6824712649431000,32,"S",120,30,"smem_native_rpm",87
+6824712648981000,0,150000,6824712649131000,711,"D",120,711,"kworker/u16:2",19725
+6824712649131000,0,113000,6824712649244000,77,"S",120,77,"smem_native_rpm",87
+6824712649244000,0,139000,6824712649383000,711,"D",120,711,"kworker/u16:2",19725
+6824712649383000,0,48000,6824712649431000,77,"S",120,77,"smem_native_rpm",87
 6824712649431000,0,72000,6824712649503000,0,"R",120,"[NULL]","swapper/5",0
-6824712649503000,0,130000,6824712649633000,17,"R+",120,16,"kworker/u16:2",19725
-6824712649633000,0,28000,6824712649661000,32,"S",120,30,"smem_native_rpm",87
-6824712649661000,0,41000,6824712649702000,17,"D",120,16,"kworker/u16:2",19725
+6824712649503000,0,130000,6824712649633000,711,"R+",120,711,"kworker/u16:2",19725
+6824712649633000,0,28000,6824712649661000,77,"S",120,77,"smem_native_rpm",87
+6824712649661000,0,41000,6824712649702000,711,"D",120,711,"kworker/u16:2",19725
 6824712649702000,0,58000,6824712649760000,0,"R",120,"[NULL]","swapper/5",0
-6824712649760000,0,262000,6824712650022000,17,"R+",120,16,"kworker/u16:2",19725
-6824712650022000,0,45000,6824712650067000,64,"S",100,50,"kworker/0:1H",558
-6824712650067000,0,34000,6824712650101000,32,"S",120,30,"smem_native_rpm",87
-6824712650101000,0,134000,6824712650235000,17,"R+",120,16,"kworker/u16:2",19725
-6824712650235000,0,27000,6824712650262000,32,"S",120,30,"smem_native_rpm",87
-6824712650262000,0,42000,6824712650304000,17,"D",120,16,"kworker/u16:2",19725
+6824712649760000,0,262000,6824712650022000,711,"R+",120,711,"kworker/u16:2",19725
+6824712650022000,0,45000,6824712650067000,458,"S",100,458,"kworker/0:1H",558
+6824712650067000,0,34000,6824712650101000,77,"S",120,77,"smem_native_rpm",87
+6824712650101000,0,134000,6824712650235000,711,"R+",120,711,"kworker/u16:2",19725
+6824712650235000,0,27000,6824712650262000,77,"S",120,77,"smem_native_rpm",87
+6824712650262000,0,42000,6824712650304000,711,"D",120,711,"kworker/u16:2",19725
 6824712650304000,0,91000,6824712650395000,0,"R",120,"[NULL]","swapper/5",0
-6824712650395000,0,177000,6824712650572000,17,"R+",120,16,"kworker/u16:2",19725
-6824712650572000,0,63000,6824712650635000,32,"S",120,30,"smem_native_rpm",87
-6824712650635000,0,222000,6824712650857000,17,"D",120,16,"kworker/u16:2",19725
+6824712650395000,0,177000,6824712650572000,711,"R+",120,711,"kworker/u16:2",19725
+6824712650572000,0,63000,6824712650635000,77,"S",120,77,"smem_native_rpm",87
+6824712650635000,0,222000,6824712650857000,711,"D",120,711,"kworker/u16:2",19725
 6824712650857000,0,1177000,6824712652034000,0,"R",120,"[NULL]","swapper/5",0
-6824712652034000,0,108000,6824712652142000,17,"R+",120,16,"kworker/u16:2",19725
-6824712652142000,0,349000,6824712652491000,16,"S",120,14,"kworker/u16:7",19422
-6824712652491000,0,379000,6824712652870000,17,"S",120,16,"kworker/u16:2",19725
-6824712652870000,0,45000,6824712652915000,16,"S",120,14,"kworker/u16:7",19422
+6824712652034000,0,108000,6824712652142000,711,"R+",120,711,"kworker/u16:2",19725
+6824712652142000,0,349000,6824712652491000,702,"S",120,702,"kworker/u16:7",19422
+6824712652491000,0,379000,6824712652870000,711,"S",120,711,"kworker/u16:2",19725
+6824712652870000,0,45000,6824712652915000,702,"S",120,702,"kworker/u16:7",19422
 6824712652915000,0,447000,6824712653362000,0,"R",120,"[NULL]","swapper/5",0
-6824712653004000,3,59000,6824712653063000,65,"S",100,51,"kworker/3:1H",578
+6824712653004000,3,59000,6824712653063000,469,"S",100,469,"kworker/3:1H",578
 6824712653063000,3,70763000,6824712723826000,0,"R",120,"[NULL]","swapper/5",0
-6824712653362000,0,46000,6824712653408000,64,"S",100,50,"kworker/0:1H",558
+6824712653362000,0,46000,6824712653408000,458,"S",100,458,"kworker/0:1H",558
 6824712653408000,0,4244000,6824712657652000,0,"R",120,"[NULL]","swapper/5",0
-6824712657652000,0,93000,6824712657745000,35,"S",120,33,"kworker/0:5",20371
-6824712657745000,0,488000,6824712658233000,36,"S",111,34,"SDM_EventThread",685
-6824712658116000,1,447000,6824712658563000,38,"S",120,35,"HwBinder:640_1",721
+6824712657652000,0,93000,6824712657745000,743,"S",120,743,"kworker/0:5",20371
+6824712657745000,0,488000,6824712658233000,786,"S",111,494,"SDM_EventThread",685
+6824712658116000,1,447000,6824712658563000,777,"S",120,493,"HwBinder:640_1",721
 6824712658233000,0,57000,6824712658290000,0,"R",120,"[NULL]","swapper/5",0
-6824712658290000,0,53000,6824712658343000,44,"S",120,37,"ksoftirqd/0",3
+6824712658290000,0,53000,6824712658343000,3,"S",120,3,"ksoftirqd/0",3
 6824712658343000,0,1697000,6824712660040000,0,"R",120,"[NULL]","swapper/5",0
-6824712658407000,2,117000,6824712658524000,40,"S",97,35,"DispSync",676
+6824712658407000,2,117000,6824712658524000,771,"S",97,493,"DispSync",676
 6824712658524000,2,2025000,6824712660549000,0,"R",120,"[NULL]","swapper/5",0
 6824712658563000,1,1537000,6824712660100000,0,"R",120,"[NULL]","swapper/5",0
-6824712660040000,0,97000,6824712660137000,35,"S",120,33,"kworker/0:5",20371
-6824712660100000,1,184000,6824712660284000,40,"S",97,35,"DispSync",676
-6824712660137000,0,391000,6824712660528000,16,"S",120,14,"kworker/u16:7",19422
+6824712660040000,0,97000,6824712660137000,743,"S",120,743,"kworker/0:5",20371
+6824712660100000,1,184000,6824712660284000,771,"S",97,493,"DispSync",676
+6824712660137000,0,391000,6824712660528000,702,"S",120,702,"kworker/u16:7",19422
 6824712660284000,1,748000,6824712661032000,0,"R",120,"[NULL]","swapper/5",0
 6824712660528000,0,434000,6824712660962000,0,"R",120,"[NULL]","swapper/5",0
-6824712660549000,2,334000,6824712660883000,41,"S",97,35,"app",678
+6824712660549000,2,334000,6824712660883000,773,"S",97,493,"app",678
 6824712660883000,2,33798000,6824712694681000,0,"R",120,"[NULL]","swapper/5",0
-6824712660962000,0,1928000,6824712662890000,42,"S",120,36,"ndroid.systemui",1664
-6824712661032000,1,82000,6824712661114000,40,"S",97,35,"DispSync",676
+6824712660962000,0,1928000,6824712662890000,644,"S",120,644,"ndroid.systemui",1664
+6824712661032000,1,82000,6824712661114000,771,"S",97,493,"DispSync",676
 6824712661114000,1,1692000,6824712662806000,0,"R",120,"[NULL]","swapper/5",0
-6824712662806000,1,304000,6824712663110000,43,"S",120,35,"Binder:640_2",675
+6824712662806000,1,304000,6824712663110000,770,"S",120,493,"Binder:640_2",675
 6824712662890000,0,402000,6824712663292000,0,"R",120,"[NULL]","swapper/5",0
 6824712663110000,1,497000,6824712663607000,0,"R",120,"[NULL]","swapper/5",0
-6824712663292000,0,190000,6824712663482000,41,"S",97,35,"app",678
+6824712663292000,0,190000,6824712663482000,773,"S",97,493,"app",678
 6824712663482000,0,57000,6824712663539000,0,"R",120,"[NULL]","swapper/5",0
-6824712663539000,0,53000,6824712663592000,44,"S",120,37,"ksoftirqd/0",3
+6824712663539000,0,53000,6824712663592000,3,"S",120,3,"ksoftirqd/0",3
 6824712663592000,0,2553000,6824712666145000,0,"R",120,"[NULL]","swapper/5",0
-6824712663607000,1,90000,6824712663697000,40,"S",97,35,"DispSync",676
+6824712663607000,1,90000,6824712663697000,771,"S",97,493,"DispSync",676
 6824712663697000,1,25560000,6824712689257000,0,"R",120,"[NULL]","swapper/5",0
-6824712666145000,0,109000,6824712666254000,32,"S",120,30,"smem_native_rpm",87
+6824712666145000,0,109000,6824712666254000,77,"S",120,77,"smem_native_rpm",87
 6824712666254000,0,23413000,6824712689667000,0,"R",120,"[NULL]","swapper/5",0
-6824712689257000,1,201000,6824712689458000,47,"S",120,40,"ksoftirqd/1",17
-6824712689458000,1,81000,6824712689539000,48,"D",120,41,"kworker/1:1",18800
+6824712689257000,1,201000,6824712689458000,15,"S",120,15,"ksoftirqd/1",17
+6824712689458000,1,81000,6824712689539000,693,"D",120,693,"kworker/1:1",18800
 6824712689539000,1,2472000,6824712692011000,0,"R",120,"[NULL]","swapper/5",0
-6824712689667000,0,99000,6824712689766000,16,"S",120,14,"kworker/u16:7",19422
+6824712689667000,0,99000,6824712689766000,702,"S",120,702,"kworker/u16:7",19422
 6824712689766000,0,1565000,6824712691331000,0,"R",120,"[NULL]","swapper/5",0
-6824712691331000,0,95000,6824712691426000,35,"S",120,33,"kworker/0:5",20371
-6824712691426000,0,518000,6824712691944000,36,"S",111,34,"SDM_EventThread",685
+6824712691331000,0,95000,6824712691426000,743,"S",120,743,"kworker/0:5",20371
+6824712691426000,0,518000,6824712691944000,786,"S",111,494,"SDM_EventThread",685
 6824712691944000,0,66000,6824712692010000,0,"R",120,"[NULL]","swapper/5",0
-6824712692010000,0,52000,6824712692062000,44,"S",120,37,"ksoftirqd/0",3
-6824712692011000,1,436000,6824712692447000,38,"S",120,35,"HwBinder:640_1",721
+6824712692010000,0,52000,6824712692062000,3,"S",120,3,"ksoftirqd/0",3
+6824712692011000,1,436000,6824712692447000,777,"S",120,493,"HwBinder:640_1",721
 6824712692062000,0,412000,6824712692474000,0,"R",120,"[NULL]","swapper/5",0
 6824712692447000,1,1171000,6824712693618000,0,"R",120,"[NULL]","swapper/5",0
-6824712692474000,0,113000,6824712692587000,40,"S",97,35,"DispSync",676
+6824712692474000,0,113000,6824712692587000,771,"S",97,493,"DispSync",676
 6824712692587000,0,1292000,6824712693879000,0,"R",120,"[NULL]","swapper/5",0
-6824712693618000,1,43000,6824712693661000,47,"S",120,40,"ksoftirqd/1",17
+6824712693618000,1,43000,6824712693661000,15,"S",120,15,"ksoftirqd/1",17
 6824712693661000,1,535000,6824712694196000,0,"R",120,"[NULL]","swapper/5",0
-6824712693879000,0,175000,6824712694054000,40,"S",97,35,"DispSync",676
+6824712693879000,0,175000,6824712694054000,771,"S",97,493,"DispSync",676
 6824712694054000,0,541000,6824712694595000,0,"R",120,"[NULL]","swapper/5",0
-6824712694196000,1,322000,6824712694518000,41,"S",97,35,"app",678
+6824712694196000,1,322000,6824712694518000,773,"S",97,493,"app",678
 6824712694518000,1,1868000,6824712696386000,0,"R",120,"[NULL]","swapper/5",0
-6824712694595000,0,1875000,6824712696470000,42,"S",120,36,"ndroid.systemui",1664
-6824712694681000,2,92000,6824712694773000,40,"S",97,35,"DispSync",676
+6824712694595000,0,1875000,6824712696470000,644,"S",120,644,"ndroid.systemui",1664
+6824712694681000,2,92000,6824712694773000,771,"S",97,493,"DispSync",676
 6824712694773000,2,2264000,6824712697037000,0,"R",120,"[NULL]","swapper/5",0
-6824712696386000,1,443000,6824712696829000,43,"S",120,35,"Binder:640_2",675
+6824712696386000,1,443000,6824712696829000,770,"S",120,493,"Binder:640_2",675
 6824712696470000,0,141000,6824712696611000,0,"R",120,"[NULL]","swapper/5",0
-6824712696611000,0,78000,6824712696689000,41,"S",97,35,"app",678
+6824712696611000,0,78000,6824712696689000,773,"S",97,493,"app",678
 6824712696689000,0,44000,6824712696733000,0,"R",120,"[NULL]","swapper/5",0
-6824712696733000,0,164000,6824712696897000,41,"S",97,35,"app",678
+6824712696733000,0,164000,6824712696897000,773,"S",97,493,"app",678
 6824712696829000,1,10308000,6824712707137000,0,"R",120,"[NULL]","swapper/5",0
 6824712696897000,0,6225000,6824712703122000,0,"R",120,"[NULL]","swapper/5",0
-6824712697037000,2,91000,6824712697128000,40,"S",97,35,"DispSync",676
+6824712697037000,2,91000,6824712697128000,771,"S",97,493,"DispSync",676
 6824712697128000,2,25635000,6824712722763000,0,"R",120,"[NULL]","swapper/5",0
-6824712703122000,0,62000,6824712703184000,16,"D",120,14,"kworker/u16:7",19422
+6824712703122000,0,62000,6824712703184000,702,"D",120,702,"kworker/u16:7",19422
 6824712703184000,0,133000,6824712703317000,0,"R",120,"[NULL]","swapper/5",0
-6824712703317000,0,21000,6824712703338000,44,"S",120,37,"ksoftirqd/0",3
-6824712703338000,0,51000,6824712703389000,35,"D",120,33,"kworker/0:5",20371
+6824712703317000,0,21000,6824712703338000,3,"S",120,3,"ksoftirqd/0",3
+6824712703338000,0,51000,6824712703389000,743,"D",120,743,"kworker/0:5",20371
 6824712703389000,0,49000,6824712703438000,0,"R",120,"[NULL]","swapper/5",0
-6824712703438000,0,613000,6824712704051000,16,"D",120,14,"kworker/u16:7",19422
-6824712704051000,0,129000,6824712704180000,32,"S",120,30,"smem_native_rpm",87
-6824712704180000,0,129000,6824712704309000,16,"D",120,14,"kworker/u16:7",19422
-6824712704309000,0,114000,6824712704423000,32,"S",120,30,"smem_native_rpm",87
-6824712704423000,0,123000,6824712704546000,16,"D",120,14,"kworker/u16:7",19422
-6824712704546000,0,107000,6824712704653000,32,"S",120,30,"smem_native_rpm",87
-6824712704653000,0,136000,6824712704789000,16,"D",120,14,"kworker/u16:7",19422
-6824712704789000,0,50000,6824712704839000,32,"S",120,30,"smem_native_rpm",87
+6824712703438000,0,613000,6824712704051000,702,"D",120,702,"kworker/u16:7",19422
+6824712704051000,0,129000,6824712704180000,77,"S",120,77,"smem_native_rpm",87
+6824712704180000,0,129000,6824712704309000,702,"D",120,702,"kworker/u16:7",19422
+6824712704309000,0,114000,6824712704423000,77,"S",120,77,"smem_native_rpm",87
+6824712704423000,0,123000,6824712704546000,702,"D",120,702,"kworker/u16:7",19422
+6824712704546000,0,107000,6824712704653000,77,"S",120,77,"smem_native_rpm",87
+6824712704653000,0,136000,6824712704789000,702,"D",120,702,"kworker/u16:7",19422
+6824712704789000,0,50000,6824712704839000,77,"S",120,77,"smem_native_rpm",87
 6824712704839000,0,628000,6824712705467000,0,"R",120,"[NULL]","swapper/5",0
-6824712705467000,0,160000,6824712705627000,16,"D",120,14,"kworker/u16:7",19422
-6824712705627000,0,121000,6824712705748000,32,"S",120,30,"smem_native_rpm",87
-6824712705748000,0,124000,6824712705872000,16,"D",120,14,"kworker/u16:7",19422
-6824712705872000,0,136000,6824712706008000,32,"S",120,30,"smem_native_rpm",87
-6824712706008000,0,152000,6824712706160000,16,"D",120,14,"kworker/u16:7",19422
-6824712706160000,0,114000,6824712706274000,32,"S",120,30,"smem_native_rpm",87
-6824712706274000,0,125000,6824712706399000,16,"D",120,14,"kworker/u16:7",19422
-6824712706399000,0,48000,6824712706447000,32,"S",120,30,"smem_native_rpm",87
+6824712705467000,0,160000,6824712705627000,702,"D",120,702,"kworker/u16:7",19422
+6824712705627000,0,121000,6824712705748000,77,"S",120,77,"smem_native_rpm",87
+6824712705748000,0,124000,6824712705872000,702,"D",120,702,"kworker/u16:7",19422
+6824712705872000,0,136000,6824712706008000,77,"S",120,77,"smem_native_rpm",87
+6824712706008000,0,152000,6824712706160000,702,"D",120,702,"kworker/u16:7",19422
+6824712706160000,0,114000,6824712706274000,77,"S",120,77,"smem_native_rpm",87
+6824712706274000,0,125000,6824712706399000,702,"D",120,702,"kworker/u16:7",19422
+6824712706399000,0,48000,6824712706447000,77,"S",120,77,"smem_native_rpm",87
 6824712706447000,0,873000,6824712707320000,0,"R",120,"[NULL]","swapper/5",0
-6824712707137000,1,57000,6824712707194000,47,"S",120,40,"ksoftirqd/1",17
+6824712707137000,1,57000,6824712707194000,15,"S",120,15,"ksoftirqd/1",17
 6824712707194000,1,15412000,6824712722606000,0,"R",120,"[NULL]","swapper/5",0
-6824712707320000,0,162000,6824712707482000,16,"D",120,14,"kworker/u16:7",19422
-6824712707482000,0,105000,6824712707587000,32,"S",120,30,"smem_native_rpm",87
-6824712707587000,0,128000,6824712707715000,16,"D",120,14,"kworker/u16:7",19422
-6824712707715000,0,115000,6824712707830000,32,"S",120,30,"smem_native_rpm",87
-6824712707830000,0,124000,6824712707954000,16,"D",120,14,"kworker/u16:7",19422
-6824712707954000,0,110000,6824712708064000,32,"S",120,30,"smem_native_rpm",87
-6824712708064000,0,140000,6824712708204000,16,"D",120,14,"kworker/u16:7",19422
-6824712708204000,0,114000,6824712708318000,32,"S",120,30,"smem_native_rpm",87
-6824712708318000,0,82000,6824712708400000,16,"S",120,14,"kworker/u16:7",19422
+6824712707320000,0,162000,6824712707482000,702,"D",120,702,"kworker/u16:7",19422
+6824712707482000,0,105000,6824712707587000,77,"S",120,77,"smem_native_rpm",87
+6824712707587000,0,128000,6824712707715000,702,"D",120,702,"kworker/u16:7",19422
+6824712707715000,0,115000,6824712707830000,77,"S",120,77,"smem_native_rpm",87
+6824712707830000,0,124000,6824712707954000,702,"D",120,702,"kworker/u16:7",19422
+6824712707954000,0,110000,6824712708064000,77,"S",120,77,"smem_native_rpm",87
+6824712708064000,0,140000,6824712708204000,702,"D",120,702,"kworker/u16:7",19422
+6824712708204000,0,114000,6824712708318000,77,"S",120,77,"smem_native_rpm",87
+6824712708318000,0,82000,6824712708400000,702,"S",120,702,"kworker/u16:7",19422
 6824712708400000,0,15285000,6824712723685000,0,"R",120,"[NULL]","swapper/5",0
-6824712722606000,1,109000,6824712722715000,66,"S",0,52,"watchdog/1",15
+6824712722606000,1,109000,6824712722715000,13,"S",0,13,"watchdog/1",15
 6824712722715000,1,79000,6824712722794000,0,"R",120,"[NULL]","swapper/5",0
-6824712722763000,2,73000,6824712722836000,67,"S",0,53,"watchdog/2",23
-6824712722794000,1,64000,6824712722858000,47,"S",120,40,"ksoftirqd/1",17
+6824712722763000,2,73000,6824712722836000,20,"S",0,20,"watchdog/2",23
+6824712722794000,1,64000,6824712722858000,15,"S",120,15,"ksoftirqd/1",17
 6824712722836000,2,5437000,6824712728273000,0,"R",120,"[NULL]","swapper/5",0
 6824712722858000,1,2789000,6824712725647000,0,"R",120,"[NULL]","swapper/5",0
-6824712723685000,0,148000,6824712723833000,46,"D",120,39,"kworker/0:1",19511
-6824712723826000,3,72000,6824712723898000,68,"S",0,54,"watchdog/3",31
+6824712723685000,0,148000,6824712723833000,705,"D",120,705,"kworker/0:1",19511
+6824712723826000,3,72000,6824712723898000,27,"S",0,27,"watchdog/3",31
 6824712723833000,0,828000,6824712724661000,0,"R",120,"[NULL]","swapper/5",0
 6824712723898000,3,34414000,6824712758312000,0,"R",120,"[NULL]","swapper/5",0
-6824712724661000,0,78000,6824712724739000,69,"S",120,55,"kworker/0:3",19397
-6824712724739000,0,236000,6824712724975000,46,"R+",120,39,"kworker/0:1",19511
-6824712724975000,0,544000,6824712725519000,36,"S",111,34,"SDM_EventThread",685
-6824712725519000,0,64000,6824712725583000,46,"S",120,39,"kworker/0:1",19511
+6824712724661000,0,78000,6824712724739000,701,"S",120,701,"kworker/0:3",19397
+6824712724739000,0,236000,6824712724975000,705,"R+",120,705,"kworker/0:1",19511
+6824712724975000,0,544000,6824712725519000,786,"S",111,494,"SDM_EventThread",685
+6824712725519000,0,64000,6824712725583000,705,"S",120,705,"kworker/0:1",19511
 6824712725583000,0,365000,6824712725948000,0,"R",120,"[NULL]","swapper/5",0
-6824712725647000,1,483000,6824712726130000,38,"S",120,35,"HwBinder:640_1",721
-6824712725948000,0,129000,6824712726077000,40,"S",97,35,"DispSync",676
+6824712725647000,1,483000,6824712726130000,777,"S",120,493,"HwBinder:640_1",721
+6824712725948000,0,129000,6824712726077000,771,"S",97,493,"DispSync",676
 6824712726077000,0,1250000,6824712727327000,0,"R",120,"[NULL]","swapper/5",0
 6824712726130000,1,1587000,6824712727717000,0,"R",120,"[NULL]","swapper/5",0
-6824712726382000,4,67000,6824712726449000,70,"S",0,56,"watchdog/4",39
+6824712726382000,4,67000,6824712726449000,34,"S",0,34,"watchdog/4",39
 6824712726449000,4,657913000,6824713384362000,0,"R",120,"[NULL]","swapper/5",0
-6824712726858000,5,46000,6824712726904000,71,"S",0,57,"watchdog/5",47
+6824712726858000,5,46000,6824712726904000,41,"S",0,41,"watchdog/5",47
 6824712726904000,5,48000,6824712726952000,0,"R",120,"[NULL]","swapper/5",0
-6824712726952000,5,38000,6824712726990000,72,"S",120,58,"ksoftirqd/5",49
+6824712726952000,5,38000,6824712726990000,43,"S",120,43,"ksoftirqd/5",49
 6824712726990000,5,678798000,6824713405788000,0,"R",120,"[NULL]","swapper/5",0
-6824712727327000,0,178000,6824712727505000,40,"S",97,35,"DispSync",676
+6824712727327000,0,178000,6824712727505000,771,"S",97,493,"DispSync",676
 6824712727505000,0,702000,6824712728207000,0,"R",120,"[NULL]","swapper/5",0
-6824712727717000,1,350000,6824712728067000,41,"S",97,35,"app",678
-6824712728062000,6,48000,6824712728110000,73,"S",0,59,"watchdog/6",55
+6824712727717000,1,350000,6824712728067000,773,"S",97,493,"app",678
+6824712728062000,6,48000,6824712728110000,48,"S",0,48,"watchdog/6",55
 6824712728067000,1,2263000,6824712730330000,0,"R",120,"[NULL]","swapper/5",0
 6824712728110000,6,110547000,6824712838657000,0,"R",120,"[NULL]","swapper/5",0
-6824712728207000,0,2150000,6824712730357000,42,"S",120,36,"ndroid.systemui",1664
-6824712728273000,2,88000,6824712728361000,40,"S",97,35,"DispSync",676
+6824712728207000,0,2150000,6824712730357000,644,"S",120,644,"ndroid.systemui",1664
+6824712728273000,2,88000,6824712728361000,771,"S",97,493,"DispSync",676
 6824712728361000,2,33537000,6824712761898000,0,"R",120,"[NULL]","swapper/5",0
-6824712729998000,7,46000,6824712730044000,74,"S",0,60,"watchdog/7",63
+6824712729998000,7,46000,6824712730044000,55,"S",0,55,"watchdog/7",63
 6824712730044000,7,136266000,6824712866310000,0,"R",120,"[NULL]","swapper/5",0
-6824712730330000,1,314000,6824712730644000,43,"S",120,35,"Binder:640_2",675
+6824712730330000,1,314000,6824712730644000,770,"S",120,493,"Binder:640_2",675
 6824712730357000,0,484000,6824712730841000,0,"R",120,"[NULL]","swapper/5",0
 6824712730644000,1,567000,6824712731211000,0,"R",120,"[NULL]","swapper/5",0
-6824712730841000,0,182000,6824712731023000,41,"S",97,35,"app",678
+6824712730841000,0,182000,6824712731023000,773,"S",97,493,"app",678
 6824712731023000,0,2119000,6824712733142000,0,"R",120,"[NULL]","swapper/5",0
-6824712731211000,1,95000,6824712731306000,40,"S",97,35,"DispSync",676
+6824712731211000,1,95000,6824712731306000,771,"S",97,493,"DispSync",676
 6824712731306000,1,27902000,6824712759208000,0,"R",120,"[NULL]","swapper/5",0
-6824712733142000,0,190000,6824712733332000,32,"S",120,30,"smem_native_rpm",87
+6824712733142000,0,190000,6824712733332000,77,"S",120,77,"smem_native_rpm",87
 6824712733332000,0,25059000,6824712758391000,0,"R",120,"[NULL]","swapper/5",0
-6824712758312000,3,37000,6824712758349000,34,"S",120,32,"ksoftirqd/3",33
-6824712758349000,3,119000,6824712758468000,16,"S",120,14,"kworker/u16:7",19422
-6824712758391000,0,96000,6824712758487000,46,"S",120,39,"kworker/0:1",19511
+6824712758312000,3,37000,6824712758349000,29,"S",120,29,"ksoftirqd/3",33
+6824712758349000,3,119000,6824712758468000,702,"S",120,702,"kworker/u16:7",19422
+6824712758391000,0,96000,6824712758487000,705,"S",120,705,"kworker/0:1",19511
 6824712758468000,3,38802000,6824712797270000,0,"R",120,"[NULL]","swapper/5",0
-6824712758487000,0,574000,6824712759061000,36,"S",111,34,"SDM_EventThread",685
+6824712758487000,0,574000,6824712759061000,786,"S",111,494,"SDM_EventThread",685
 6824712759061000,0,699000,6824712759760000,0,"R",120,"[NULL]","swapper/5",0
-6824712759208000,1,489000,6824712759697000,38,"S",120,35,"HwBinder:640_1",721
+6824712759208000,1,489000,6824712759697000,777,"S",120,493,"HwBinder:640_1",721
 6824712759697000,1,1610000,6824712761307000,0,"R",120,"[NULL]","swapper/5",0
-6824712759760000,0,131000,6824712759891000,40,"S",97,35,"DispSync",676
+6824712759760000,0,131000,6824712759891000,771,"S",97,493,"DispSync",676
 6824712759891000,0,227000,6824712760118000,0,"R",120,"[NULL]","swapper/5",0
-6824712760118000,0,54000,6824712760172000,46,"S",120,39,"kworker/0:1",19511
-6824712760172000,0,509000,6824712760681000,16,"S",120,14,"kworker/u16:7",19422
+6824712760118000,0,54000,6824712760172000,705,"S",120,705,"kworker/0:1",19511
+6824712760172000,0,509000,6824712760681000,702,"S",120,702,"kworker/u16:7",19422
 6824712760681000,0,231000,6824712760912000,0,"R",120,"[NULL]","swapper/5",0
-6824712760912000,0,182000,6824712761094000,40,"S",97,35,"DispSync",676
+6824712760912000,0,182000,6824712761094000,771,"S",97,493,"DispSync",676
 6824712761094000,0,708000,6824712761802000,0,"R",120,"[NULL]","swapper/5",0
-6824712761307000,1,363000,6824712761670000,41,"S",97,35,"app",678
+6824712761307000,1,363000,6824712761670000,773,"S",97,493,"app",678
 6824712761670000,1,2291000,6824712763961000,0,"R",120,"[NULL]","swapper/5",0
-6824712761802000,0,2190000,6824712763992000,42,"S",120,36,"ndroid.systemui",1664
-6824712761898000,2,92000,6824712761990000,40,"S",97,35,"DispSync",676
+6824712761802000,0,2190000,6824712763992000,644,"S",120,644,"ndroid.systemui",1664
+6824712761898000,2,92000,6824712761990000,771,"S",97,493,"DispSync",676
 6824712761990000,2,35024000,6824712797014000,0,"R",120,"[NULL]","swapper/5",0
-6824712763961000,1,312000,6824712764273000,43,"S",120,35,"Binder:640_2",675
+6824712763961000,1,312000,6824712764273000,770,"S",120,493,"Binder:640_2",675
 6824712763992000,0,457000,6824712764449000,0,"R",120,"[NULL]","swapper/5",0
 6824712764273000,1,539000,6824712764812000,0,"R",120,"[NULL]","swapper/5",0
-6824712764449000,0,182000,6824712764631000,41,"S",97,35,"app",678
+6824712764449000,0,182000,6824712764631000,773,"S",97,493,"app",678
 6824712764631000,0,30567000,6824712795198000,0,"R",120,"[NULL]","swapper/5",0
-6824712764812000,1,95000,6824712764907000,40,"S",97,35,"DispSync",676
+6824712764812000,1,95000,6824712764907000,771,"S",97,493,"DispSync",676
 6824712764907000,1,30302000,6824712795209000,0,"R",120,"[NULL]","swapper/5",0
-6824712795198000,0,480000,6824712795678000,40,"S",97,35,"DispSync",676
-6824712795209000,1,163000,6824712795372000,47,"S",120,40,"ksoftirqd/1",17
+6824712795198000,0,480000,6824712795678000,771,"S",97,493,"DispSync",676
+6824712795209000,1,163000,6824712795372000,15,"S",120,15,"ksoftirqd/1",17
 6824712795372000,1,877000,6824712796249000,0,"R",120,"[NULL]","swapper/5",0
-6824712795678000,0,171000,6824712795849000,46,"S",120,39,"kworker/0:1",19511
-6824712795849000,0,1068000,6824712796917000,36,"S",111,34,"SDM_EventThread",685
-6824712796249000,1,683000,6824712796932000,41,"S",97,35,"app",678
+6824712795678000,0,171000,6824712795849000,705,"S",120,705,"kworker/0:1",19511
+6824712795849000,0,1068000,6824712796917000,786,"S",111,494,"SDM_EventThread",685
+6824712796249000,1,683000,6824712796932000,773,"S",97,493,"app",678
 6824712796917000,0,862000,6824712797779000,0,"R",120,"[NULL]","swapper/5",0
 6824712796932000,1,4294000,6824712801226000,0,"R",120,"[NULL]","swapper/5",0
-6824712797014000,2,615000,6824712797629000,38,"S",120,35,"HwBinder:640_1",721
-6824712797270000,3,69000,6824712797339000,40,"S",97,35,"DispSync",676
-6824712797339000,3,3182000,6824712800521000,42,"S",120,36,"ndroid.systemui",1664
+6824712797014000,2,615000,6824712797629000,777,"S",120,493,"HwBinder:640_1",721
+6824712797270000,3,69000,6824712797339000,771,"S",97,493,"DispSync",676
+6824712797339000,3,3182000,6824712800521000,644,"S",120,644,"ndroid.systemui",1664
 6824712797629000,2,32555000,6824712830184000,0,"R",120,"[NULL]","swapper/5",0
-6824712797779000,0,87000,6824712797866000,40,"S",97,35,"DispSync",676
+6824712797779000,0,87000,6824712797866000,771,"S",97,493,"DispSync",676
 6824712797866000,0,2676000,6824712800542000,0,"R",120,"[NULL]","swapper/5",0
 6824712800521000,3,61041000,6824712861562000,0,"R",120,"[NULL]","swapper/5",0
-6824712800542000,0,397000,6824712800939000,43,"S",120,35,"Binder:640_2",675
+6824712800542000,0,397000,6824712800939000,770,"S",120,493,"Binder:640_2",675
 6824712800939000,0,790000,6824712801729000,0,"R",120,"[NULL]","swapper/5",0
-6824712801226000,1,203000,6824712801429000,41,"S",97,35,"app",678
+6824712801226000,1,203000,6824712801429000,773,"S",97,493,"app",678
 6824712801429000,1,28175000,6824712829604000,0,"R",120,"[NULL]","swapper/5",0
-6824712801729000,0,88000,6824712801817000,40,"S",97,35,"DispSync",676
+6824712801729000,0,88000,6824712801817000,771,"S",97,493,"DispSync",676
 6824712801817000,0,26133000,6824712827950000,0,"R",120,"[NULL]","swapper/5",0
-6824712827950000,0,303000,6824712828253000,46,"S",120,39,"kworker/0:1",19511
-6824712828253000,0,1174000,6824712829427000,36,"S",111,34,"SDM_EventThread",685
+6824712827950000,0,303000,6824712828253000,705,"S",120,705,"kworker/0:1",19511
+6824712828253000,0,1174000,6824712829427000,786,"S",111,494,"SDM_EventThread",685
 6824712829427000,0,87000,6824712829514000,0,"R",120,"[NULL]","swapper/5",0
-6824712829514000,0,249000,6824712829763000,44,"S",120,37,"ksoftirqd/0",3
-6824712829604000,1,771000,6824712830375000,38,"S",120,35,"HwBinder:640_1",721
-6824712829763000,0,134000,6824712829897000,16,"S",120,14,"kworker/u16:7",19422
+6824712829514000,0,249000,6824712829763000,3,"S",120,3,"ksoftirqd/0",3
+6824712829604000,1,771000,6824712830375000,777,"S",120,493,"HwBinder:640_1",721
+6824712829763000,0,134000,6824712829897000,702,"S",120,702,"kworker/u16:7",19422
 6824712829897000,0,1050000,6824712830947000,0,"R",120,"[NULL]","swapper/5",0
-6824712830184000,2,302000,6824712830486000,40,"S",97,35,"DispSync",676
+6824712830184000,2,302000,6824712830486000,771,"S",97,493,"DispSync",676
 6824712830375000,1,1386000,6824712831761000,0,"R",120,"[NULL]","swapper/5",0
 6824712830486000,2,1407000,6824712831893000,0,"R",120,"[NULL]","swapper/5",0
-6824712830947000,0,531000,6824712831478000,41,"S",97,35,"app",678
+6824712830947000,0,531000,6824712831478000,773,"S",97,493,"app",678
 6824712831478000,0,3444000,6824712834922000,0,"R",120,"[NULL]","swapper/5",0
-6824712831761000,1,3128000,6824712834889000,42,"S",120,36,"ndroid.systemui",1664
-6824712831893000,2,86000,6824712831979000,40,"S",97,35,"DispSync",676
+6824712831761000,1,3128000,6824712834889000,644,"S",120,644,"ndroid.systemui",1664
+6824712831893000,2,86000,6824712831979000,771,"S",97,493,"DispSync",676
 6824712831979000,2,9415000,6824712841394000,0,"R",120,"[NULL]","swapper/5",0
 6824712834889000,1,707000,6824712835596000,0,"R",120,"[NULL]","swapper/5",0
-6824712834922000,0,398000,6824712835320000,43,"S",120,35,"Binder:640_2",675
+6824712834922000,0,398000,6824712835320000,770,"S",120,493,"Binder:640_2",675
 6824712835320000,0,775000,6824712836095000,0,"R",120,"[NULL]","swapper/5",0
-6824712835596000,1,201000,6824712835797000,41,"S",97,35,"app",678
+6824712835596000,1,201000,6824712835797000,773,"S",97,493,"app",678
 6824712835797000,1,24848000,6824712860645000,0,"R",120,"[NULL]","swapper/5",0
-6824712836095000,0,93000,6824712836188000,40,"S",97,35,"DispSync",676
+6824712836095000,0,93000,6824712836188000,771,"S",97,493,"DispSync",676
 6824712836188000,0,23047000,6824712859235000,0,"R",120,"[NULL]","swapper/5",0
-6824712838657000,6,193000,6824712838850000,2,"S",100,2,"kworker/u17:1",1134
+6824712838657000,6,193000,6824712838850000,630,"S",100,630,"kworker/u17:1",1134
 6824712838850000,6,70000,6824712838920000,0,"R",120,"[NULL]","swapper/5",0
-6824712838920000,6,49000,6824712838969000,45,"S",120,38,"ksoftirqd/6",57
+6824712838920000,6,49000,6824712838969000,50,"S",120,50,"ksoftirqd/6",57
 6824712838969000,6,3751000,6824712842720000,0,"R",120,"[NULL]","swapper/5",0
-6824712841394000,2,165000,6824712841559000,25,"S",120,24,"ksoftirqd/2",25
+6824712841394000,2,165000,6824712841559000,22,"S",120,22,"ksoftirqd/2",25
 6824712841559000,2,3497000,6824712845056000,0,"R",120,"[NULL]","swapper/5",0
-6824712842720000,6,303000,6824712843023000,2,"S",100,2,"kworker/u17:1",1134
-6824712843023000,6,564000,6824712843587000,3,"S",120,3,"kworker/6:1",14833
+6824712842720000,6,303000,6824712843023000,630,"S",100,630,"kworker/u17:1",1134
+6824712843023000,6,564000,6824712843587000,669,"S",120,669,"kworker/6:1",14833
 6824712843587000,6,2780000,6824712846367000,0,"R",120,"[NULL]","swapper/5",0
-6824712845056000,2,686000,6824712845742000,4,"S",120,5,"UsbFfs-worker",20308
+6824712845056000,2,686000,6824712845742000,2487,"S",120,739,"UsbFfs-worker",20308
 6824712845742000,2,3405000,6824712849147000,0,"R",120,"[NULL]","swapper/5",0
-6824712846367000,6,406000,6824712846773000,2,"S",100,2,"kworker/u17:1",1134
-6824712846773000,6,355000,6824712847128000,3,"S",120,3,"kworker/6:1",14833
+6824712846367000,6,406000,6824712846773000,630,"S",100,630,"kworker/u17:1",1134
+6824712846773000,6,355000,6824712847128000,669,"S",120,669,"kworker/6:1",14833
 6824712847128000,6,206037000,6824713053165000,0,"R",120,"[NULL]","swapper/5",0
-6824712849147000,2,1203000,6824712850350000,4,"S",120,5,"UsbFfs-worker",20308
-6824712850350000,2,3128000,6824712853478000,7,"R+",120,5,"adbd",20305
-6824712853478000,2,128000,6824712853606000,23,"S",120,21,"rcuop/2",28
-6824712853606000,2,62000,6824712853668000,6,"S",120,4,"rcu_preempt",7
-6824712853668000,2,3944000,6824712857612000,7,"S",120,5,"adbd",20305
-6824712857612000,2,2594000,6824712860206000,75,"R+",120,61,"sh",20457
-6824712859235000,0,137000,6824712859372000,46,"S",120,39,"kworker/0:1",19511
-6824712859372000,0,965000,6824712860337000,36,"S",111,34,"SDM_EventThread",685
-6824712860206000,2,85000,6824712860291000,31,"S",120,29,"kworker/2:0",18823
-6824712860291000,2,57000,6824712860348000,6,"S",120,4,"rcu_preempt",7
-6824712860337000,0,922000,6824712861259000,16,"S",120,14,"kworker/u16:7",19422
-6824712860348000,2,3024000,6824712863372000,75,"R+",120,61,"sh",20457
-6824712860645000,1,709000,6824712861354000,38,"S",120,35,"HwBinder:640_1",721
+6824712849147000,2,1203000,6824712850350000,2487,"S",120,739,"UsbFfs-worker",20308
+6824712850350000,2,3128000,6824712853478000,739,"R+",120,739,"adbd",20305
+6824712853478000,2,128000,6824712853606000,24,"S",120,24,"rcuop/2",28
+6824712853606000,2,62000,6824712853668000,5,"S",120,5,"rcu_preempt",7
+6824712853668000,2,3944000,6824712857612000,739,"S",120,739,"adbd",20305
+6824712857612000,2,2594000,6824712860206000,2733,"R+",120,761,"sh",20457
+6824712859235000,0,137000,6824712859372000,705,"S",120,705,"kworker/0:1",19511
+6824712859372000,0,965000,6824712860337000,786,"S",111,494,"SDM_EventThread",685
+6824712860206000,2,85000,6824712860291000,694,"S",120,694,"kworker/2:0",18823
+6824712860291000,2,57000,6824712860348000,5,"S",120,5,"rcu_preempt",7
+6824712860337000,0,922000,6824712861259000,702,"S",120,702,"kworker/u16:7",19422
+6824712860348000,2,3024000,6824712863372000,2733,"R+",120,761,"sh",20457
+6824712860645000,1,709000,6824712861354000,777,"S",120,493,"HwBinder:640_1",721
 6824712861259000,0,553000,6824712861812000,0,"R",120,"[NULL]","swapper/5",0
 6824712861354000,1,1038000,6824712862392000,0,"R",120,"[NULL]","swapper/5",0
-6824712861562000,3,169000,6824712861731000,40,"S",97,35,"DispSync",676
+6824712861562000,3,169000,6824712861731000,771,"S",97,493,"DispSync",676
 6824712861731000,3,1521000,6824712863252000,0,"R",120,"[NULL]","swapper/5",0
-6824712861812000,0,223000,6824712862035000,40,"S",97,35,"DispSync",676
+6824712861812000,0,223000,6824712862035000,771,"S",97,493,"DispSync",676
 6824712862035000,0,716000,6824712862751000,0,"R",120,"[NULL]","swapper/5",0
-6824712862392000,1,491000,6824712862883000,41,"S",97,35,"app",678
-6824712862751000,0,3243000,6824712865994000,42,"S",120,36,"ndroid.systemui",1664
+6824712862392000,1,491000,6824712862883000,773,"S",97,493,"app",678
+6824712862751000,0,3243000,6824712865994000,644,"S",120,644,"ndroid.systemui",1664
 6824712862883000,1,3367000,6824712866250000,0,"R",120,"[NULL]","swapper/5",0
-6824712863252000,3,63000,6824712863315000,40,"S",97,35,"DispSync",676
-6824712863315000,3,230000,6824712863545000,20,"S",120,18,"rcuos/2",29
-6824712863372000,2,95000,6824712863467000,6,"S",120,4,"rcu_preempt",7
-6824712863467000,2,80000,6824712863547000,23,"S",120,21,"rcuop/2",28
+6824712863252000,3,63000,6824712863315000,771,"S",97,493,"DispSync",676
+6824712863315000,3,230000,6824712863545000,25,"S",120,25,"rcuos/2",29
+6824712863372000,2,95000,6824712863467000,5,"S",120,5,"rcu_preempt",7
+6824712863467000,2,80000,6824712863547000,24,"S",120,24,"rcuop/2",28
 6824712863545000,3,13874000,6824712877419000,0,"R",120,"[NULL]","swapper/5",0
-6824712863547000,2,40000,6824712863587000,5,"S",120,7,"rcu_sched",8
-6824712863587000,2,24000,6824712863611000,6,"S",120,4,"rcu_preempt",7
-6824712863611000,2,1548000,6824712865159000,75,"R+",120,61,"sh",20457
-6824712865159000,2,3064000,6824712868223000,7,"R+",120,5,"adbd",20305
+6824712863547000,2,40000,6824712863587000,6,"S",120,6,"rcu_sched",8
+6824712863587000,2,24000,6824712863611000,5,"S",120,5,"rcu_preempt",7
+6824712863611000,2,1548000,6824712865159000,2733,"R+",120,761,"sh",20457
+6824712865159000,2,3064000,6824712868223000,739,"R+",120,739,"adbd",20305
 6824712865994000,0,1047000,6824712867041000,0,"R",120,"[NULL]","swapper/5",0
-6824712866250000,1,587000,6824712866837000,43,"S",120,35,"Binder:640_2",675
-6824712866310000,7,74000,6824712866384000,76,"S",120,62,"hwrng",215
+6824712866250000,1,587000,6824712866837000,770,"S",120,493,"Binder:640_2",675
+6824712866310000,7,74000,6824712866384000,145,"S",120,145,"hwrng",215
 6824712866384000,7,6718000,6824712873102000,0,"R",120,"[NULL]","swapper/5",0
 6824712866837000,1,771000,6824712867608000,0,"R",120,"[NULL]","swapper/5",0
-6824712867041000,0,285000,6824712867326000,41,"S",97,35,"app",678
+6824712867041000,0,285000,6824712867326000,773,"S",97,493,"app",678
 6824712867326000,0,9974000,6824712877300000,0,"R",120,"[NULL]","swapper/5",0
-6824712867608000,1,76000,6824712867684000,40,"S",97,35,"DispSync",676
-6824712867684000,1,110000,6824712867794000,2,"S",100,2,"kworker/u17:1",1134
+6824712867608000,1,76000,6824712867684000,771,"S",97,493,"DispSync",676
+6824712867684000,1,110000,6824712867794000,630,"S",100,630,"kworker/u17:1",1134
 6824712867794000,1,62000,6824712867856000,0,"R",120,"[NULL]","swapper/5",0
-6824712867856000,1,140000,6824712867996000,2,"S",100,2,"kworker/u17:1",1134
-6824712867996000,1,60000,6824712868056000,77,"R+",120,63,"kworker/1:0",19220
-6824712868056000,1,59000,6824712868115000,2,"S",100,2,"kworker/u17:1",1134
-6824712868115000,1,111000,6824712868226000,77,"R+",120,63,"kworker/1:0",19220
-6824712868223000,2,292000,6824712868515000,4,"S",120,5,"UsbFfs-worker",20308
-6824712868226000,1,78000,6824712868304000,2,"S",100,2,"kworker/u17:1",1134
-6824712868304000,1,48000,6824712868352000,77,"R+",120,63,"kworker/1:0",19220
-6824712868352000,1,57000,6824712868409000,2,"S",100,2,"kworker/u17:1",1134
-6824712868409000,1,45000,6824712868454000,77,"R+",120,63,"kworker/1:0",19220
-6824712868454000,1,72000,6824712868526000,2,"S",100,2,"kworker/u17:1",1134
-6824712868515000,2,145000,6824712868660000,7,"S",120,5,"adbd",20305
-6824712868526000,1,257000,6824712868783000,77,"S",120,63,"kworker/1:0",19220
-6824712868660000,2,454000,6824712869114000,4,"S",120,5,"UsbFfs-worker",20308
+6824712867856000,1,140000,6824712867996000,630,"S",100,630,"kworker/u17:1",1134
+6824712867996000,1,60000,6824712868056000,698,"R+",120,698,"kworker/1:0",19220
+6824712868056000,1,59000,6824712868115000,630,"S",100,630,"kworker/u17:1",1134
+6824712868115000,1,111000,6824712868226000,698,"R+",120,698,"kworker/1:0",19220
+6824712868223000,2,292000,6824712868515000,2487,"S",120,739,"UsbFfs-worker",20308
+6824712868226000,1,78000,6824712868304000,630,"S",100,630,"kworker/u17:1",1134
+6824712868304000,1,48000,6824712868352000,698,"R+",120,698,"kworker/1:0",19220
+6824712868352000,1,57000,6824712868409000,630,"S",100,630,"kworker/u17:1",1134
+6824712868409000,1,45000,6824712868454000,698,"R+",120,698,"kworker/1:0",19220
+6824712868454000,1,72000,6824712868526000,630,"S",100,630,"kworker/u17:1",1134
+6824712868515000,2,145000,6824712868660000,739,"S",120,739,"adbd",20305
+6824712868526000,1,257000,6824712868783000,698,"S",120,698,"kworker/1:0",19220
+6824712868660000,2,454000,6824712869114000,2487,"S",120,739,"UsbFfs-worker",20308
 6824712868783000,1,1322000,6824712870105000,0,"R",120,"[NULL]","swapper/5",0
-6824712869114000,2,770000,6824712869884000,7,"S",120,5,"adbd",20305
-6824712869884000,2,197000,6824712870081000,75,"R+",120,61,"sh",20457
-6824712870081000,2,56000,6824712870137000,6,"S",120,4,"rcu_preempt",7
-6824712870105000,1,117000,6824712870222000,2,"S",100,2,"kworker/u17:1",1134
-6824712870137000,2,41000,6824712870178000,5,"S",120,7,"rcu_sched",8
-6824712870178000,2,153000,6824712870331000,75,"R+",120,61,"sh",20457
-6824712870222000,1,113000,6824712870335000,77,"R+",120,63,"kworker/1:0",19220
-6824712870331000,2,69000,6824712870400000,4,"S",120,5,"UsbFfs-worker",20308
-6824712870335000,1,43000,6824712870378000,2,"S",100,2,"kworker/u17:1",1134
-6824712870378000,1,66000,6824712870444000,77,"S",120,63,"kworker/1:0",19220
-6824712870400000,2,6436000,6824712876836000,75,"R+",120,61,"sh",20457
+6824712869114000,2,770000,6824712869884000,739,"S",120,739,"adbd",20305
+6824712869884000,2,197000,6824712870081000,2733,"R+",120,761,"sh",20457
+6824712870081000,2,56000,6824712870137000,5,"S",120,5,"rcu_preempt",7
+6824712870105000,1,117000,6824712870222000,630,"S",100,630,"kworker/u17:1",1134
+6824712870137000,2,41000,6824712870178000,6,"S",120,6,"rcu_sched",8
+6824712870178000,2,153000,6824712870331000,2733,"R+",120,761,"sh",20457
+6824712870222000,1,113000,6824712870335000,698,"R+",120,698,"kworker/1:0",19220
+6824712870331000,2,69000,6824712870400000,2487,"S",120,739,"UsbFfs-worker",20308
+6824712870335000,1,43000,6824712870378000,630,"S",100,630,"kworker/u17:1",1134
+6824712870378000,1,66000,6824712870444000,698,"S",120,698,"kworker/1:0",19220
+6824712870400000,2,6436000,6824712876836000,2733,"R+",120,761,"sh",20457
 6824712870444000,1,20085000,6824712890529000,0,"R",120,"[NULL]","swapper/5",0
-6824712873102000,7,72000,6824712873174000,76,"S",120,62,"hwrng",215
+6824712873102000,7,72000,6824712873174000,145,"S",120,145,"hwrng",215
 6824712873174000,7,5793000,6824712878967000,0,"R",120,"[NULL]","swapper/5",0
-6824712876836000,2,146000,6824712876982000,5,"S",120,7,"rcu_sched",8
-6824712876982000,2,88000,6824712877070000,6,"S",120,4,"rcu_preempt",7
-6824712877070000,2,86000,6824712877156000,23,"S",120,21,"rcuop/2",28
-6824712877156000,2,40000,6824712877196000,6,"S",120,4,"rcu_preempt",7
-6824712877196000,2,374000,6824712877570000,78,"R",120,5,"shell",20458
-6824712877300000,0,156000,6824712877456000,16,"S",120,14,"kworker/u16:7",19422
-6824712877419000,3,189000,6824712877608000,20,"S",120,18,"rcuos/2",29
+6824712876836000,2,146000,6824712876982000,6,"S",120,6,"rcu_sched",8
+6824712876982000,2,88000,6824712877070000,5,"S",120,5,"rcu_preempt",7
+6824712877070000,2,86000,6824712877156000,24,"S",120,24,"rcuop/2",28
+6824712877156000,2,40000,6824712877196000,5,"S",120,5,"rcu_preempt",7
+6824712877196000,2,374000,6824712877570000,2734,"R",120,739,"shell",20458
+6824712877300000,0,156000,6824712877456000,702,"S",120,702,"kworker/u16:7",19422
+6824712877419000,3,189000,6824712877608000,25,"S",120,25,"rcuos/2",29
 6824712877456000,0,6419000,6824712883875000,0,"R",120,"[NULL]","swapper/5",0
-6824712877570000,2,32000,6824712877602000,5,"S",120,7,"rcu_sched",8
-6824712877602000,2,441000,6824712878043000,78,"S",120,5,"shell",20458
+6824712877570000,2,32000,6824712877602000,6,"S",120,6,"rcu_sched",8
+6824712877602000,2,441000,6824712878043000,2734,"S",120,739,"shell",20458
 6824712877608000,3,13785000,6824712891393000,0,"R",120,"[NULL]","swapper/5",0
-6824712878043000,2,5493000,6824712883536000,75,"R+",120,61,"sh",20457
-6824712878967000,7,74000,6824712879041000,76,"S",120,62,"hwrng",215
+6824712878043000,2,5493000,6824712883536000,2733,"R+",120,761,"sh",20457
+6824712878967000,7,74000,6824712879041000,145,"S",120,145,"hwrng",215
 6824712879041000,7,22599000,6824712901640000,0,"R",120,"[NULL]","swapper/5",0
-6824712883536000,2,224000,6824712883760000,26,"S",49,23,"sugov:0",605
-6824712883760000,2,7130000,6824712890890000,75,"R",120,61,"sh",20457
-6824712883875000,0,37000,6824712883912000,5,"S",120,7,"rcu_sched",8
-6824712883912000,0,38000,6824712883950000,6,"S",120,4,"rcu_preempt",7
+6824712883536000,2,224000,6824712883760000,482,"S",49,482,"sugov:0",605
+6824712883760000,2,7130000,6824712890890000,2733,"R",120,761,"sh",20457
+6824712883875000,0,37000,6824712883912000,6,"S",120,6,"rcu_sched",8
+6824712883912000,0,38000,6824712883950000,5,"S",120,5,"rcu_preempt",7
 6824712883950000,0,6876000,6824712890826000,0,"R",120,"[NULL]","swapper/5",0
-6824712890529000,1,61000,6824712890590000,48,"S",120,41,"kworker/1:1",18800
+6824712890529000,1,61000,6824712890590000,693,"S",120,693,"kworker/1:1",18800
 6824712890590000,1,2884000,6824712893474000,0,"R",120,"[NULL]","swapper/5",0
-6824712890826000,0,72000,6824712890898000,6,"S",120,4,"rcu_preempt",7
-6824712890890000,2,188000,6824712891078000,23,"S",120,21,"rcuop/2",28
-6824712890898000,0,80000,6824712890978000,5,"S",120,7,"rcu_sched",8
+6824712890826000,0,72000,6824712890898000,5,"S",120,5,"rcu_preempt",7
+6824712890890000,2,188000,6824712891078000,24,"S",120,24,"rcuop/2",28
+6824712890898000,0,80000,6824712890978000,6,"S",120,6,"rcu_sched",8
 6824712890978000,0,595000,6824712891573000,0,"R",120,"[NULL]","swapper/5",0
-6824712891078000,2,5651000,6824712896729000,75,"R",120,61,"sh",20457
-6824712891393000,3,58000,6824712891451000,20,"S",120,18,"rcuos/2",29
+6824712891078000,2,5651000,6824712896729000,2733,"R",120,761,"sh",20457
+6824712891393000,3,58000,6824712891451000,25,"S",120,25,"rcuos/2",29
 6824712891451000,3,4407000,6824712895858000,0,"R",120,"[NULL]","swapper/5",0
-6824712891573000,0,40000,6824712891613000,6,"S",120,4,"rcu_preempt",7
+6824712891573000,0,40000,6824712891613000,5,"S",120,5,"rcu_preempt",7
 6824712891613000,0,803000,6824712892416000,0,"R",120,"[NULL]","swapper/5",0
-6824712892416000,0,105000,6824712892521000,46,"S",120,39,"kworker/0:1",19511
-6824712892521000,0,800000,6824712893321000,36,"S",111,34,"SDM_EventThread",685
+6824712892416000,0,105000,6824712892521000,705,"S",120,705,"kworker/0:1",19511
+6824712892521000,0,800000,6824712893321000,786,"S",111,494,"SDM_EventThread",685
 6824712893321000,0,850000,6824712894171000,0,"R",120,"[NULL]","swapper/5",0
-6824712893474000,1,566000,6824712894040000,38,"S",120,35,"HwBinder:640_1",721
+6824712893474000,1,566000,6824712894040000,777,"S",120,493,"HwBinder:640_1",721
 6824712894040000,1,1068000,6824712895108000,0,"R",120,"[NULL]","swapper/5",0
-6824712894171000,0,111000,6824712894282000,40,"S",97,35,"DispSync",676
+6824712894171000,0,111000,6824712894282000,771,"S",97,493,"DispSync",676
 6824712894282000,0,714000,6824712894996000,0,"R",120,"[NULL]","swapper/5",0
-6824712894996000,0,140000,6824712895136000,40,"S",97,35,"DispSync",676
-6824712895108000,1,410000,6824712895518000,41,"S",97,35,"app",678
+6824712894996000,0,140000,6824712895136000,771,"S",97,493,"DispSync",676
+6824712895108000,1,410000,6824712895518000,773,"S",97,493,"app",678
 6824712895136000,0,301000,6824712895437000,0,"R",120,"[NULL]","swapper/5",0
-6824712895437000,0,1223000,6824712896660000,42,"R",120,36,"ndroid.systemui",1664
+6824712895437000,0,1223000,6824712896660000,644,"R",120,644,"ndroid.systemui",1664
 6824712895518000,1,2511000,6824712898029000,0,"R",120,"[NULL]","swapper/5",0
-6824712895858000,3,55000,6824712895913000,40,"S",97,35,"DispSync",676
+6824712895858000,3,55000,6824712895913000,771,"S",97,493,"DispSync",676
 6824712895913000,3,20866000,6824712916779000,0,"R",120,"[NULL]","swapper/5",0
-6824712896660000,0,76000,6824712896736000,6,"S",120,4,"rcu_preempt",7
-6824712896729000,2,38000,6824712896767000,23,"S",120,21,"rcuop/2",28
-6824712896736000,0,1181000,6824712897917000,42,"S",120,36,"ndroid.systemui",1664
-6824712896767000,2,3122000,6824712899889000,75,"R",120,61,"sh",20457
+6824712896660000,0,76000,6824712896736000,5,"S",120,5,"rcu_preempt",7
+6824712896729000,2,38000,6824712896767000,24,"S",120,24,"rcuop/2",28
+6824712896736000,0,1181000,6824712897917000,644,"S",120,644,"ndroid.systemui",1664
+6824712896767000,2,3122000,6824712899889000,2733,"R",120,761,"sh",20457
 6824712897917000,0,362000,6824712898279000,0,"R",120,"[NULL]","swapper/5",0
-6824712898029000,1,330000,6824712898359000,43,"S",120,35,"Binder:640_2",675
-6824712898279000,0,181000,6824712898460000,41,"S",97,35,"app",678
+6824712898029000,1,330000,6824712898359000,770,"S",120,493,"Binder:640_2",675
+6824712898279000,0,181000,6824712898460000,773,"S",97,493,"app",678
 6824712898359000,1,40000,6824712898399000,0,"R",120,"[NULL]","swapper/5",0
-6824712898399000,1,53000,6824712898452000,40,"S",97,35,"DispSync",676
+6824712898399000,1,53000,6824712898452000,771,"S",97,493,"DispSync",676
 6824712898452000,1,28312000,6824712926764000,0,"R",120,"[NULL]","swapper/5",0
 6824712898460000,0,1923000,6824712900383000,0,"R",120,"[NULL]","swapper/5",0
-6824712899889000,2,124000,6824712900013000,23,"S",120,21,"rcuop/2",28
-6824712900013000,2,3333000,6824712903346000,75,"R",120,61,"sh",20457
-6824712900383000,0,51000,6824712900434000,6,"S",120,4,"rcu_preempt",7
+6824712899889000,2,124000,6824712900013000,24,"S",120,24,"rcuop/2",28
+6824712900013000,2,3333000,6824712903346000,2733,"R",120,761,"sh",20457
+6824712900383000,0,51000,6824712900434000,5,"S",120,5,"rcu_preempt",7
 6824712900434000,0,6845000,6824712907279000,0,"R",120,"[NULL]","swapper/5",0
-6824712901640000,7,72000,6824712901712000,76,"S",120,62,"hwrng",215
+6824712901640000,7,72000,6824712901712000,145,"S",120,145,"hwrng",215
 6824712901712000,7,80781000,6824712982493000,0,"R",120,"[NULL]","swapper/5",0
-6824712903346000,2,197000,6824712903543000,26,"S",49,23,"sugov:0",605
-6824712903543000,2,3857000,6824712907400000,75,"R",120,61,"sh",20457
-6824712907279000,0,57000,6824712907336000,35,"S",120,33,"kworker/0:5",20371
-6824712907336000,0,77000,6824712907413000,6,"S",120,4,"rcu_preempt",7
-6824712907400000,2,66000,6824712907466000,23,"S",120,21,"rcuop/2",28
+6824712903346000,2,197000,6824712903543000,482,"S",49,482,"sugov:0",605
+6824712903543000,2,3857000,6824712907400000,2733,"R",120,761,"sh",20457
+6824712907279000,0,57000,6824712907336000,743,"S",120,743,"kworker/0:5",20371
+6824712907336000,0,77000,6824712907413000,5,"S",120,5,"rcu_preempt",7
+6824712907400000,2,66000,6824712907466000,24,"S",120,24,"rcuop/2",28
 6824712907413000,0,51000,6824712907464000,0,"R",120,"[NULL]","swapper/5",0
-6824712907464000,0,27000,6824712907491000,6,"S",120,4,"rcu_preempt",7
-6824712907466000,2,180000,6824712907646000,75,"R+",120,61,"sh",20457
+6824712907464000,0,27000,6824712907491000,5,"S",120,5,"rcu_preempt",7
+6824712907466000,2,180000,6824712907646000,2733,"R+",120,761,"sh",20457
 6824712907491000,0,6442000,6824712913933000,0,"R",120,"[NULL]","swapper/5",0
-6824712907646000,2,198000,6824712907844000,78,"S",120,5,"shell",20458
-6824712907844000,2,962000,6824712908806000,7,"R+",120,5,"adbd",20305
-6824712908806000,2,188000,6824712908994000,2,"S",100,2,"kworker/u17:1",1134
-6824712908994000,2,81000,6824712909075000,31,"S",120,29,"kworker/2:0",18823
-6824712909075000,2,25000,6824712909100000,79,"S",100,64,"kworker/u17:2",14944
-6824712909100000,2,79000,6824712909179000,4,"S",120,5,"UsbFfs-worker",20308
-6824712909179000,2,99000,6824712909278000,7,"R+",120,5,"adbd",20305
-6824712909278000,2,50000,6824712909328000,79,"S",100,64,"kworker/u17:2",14944
-6824712909328000,2,49000,6824712909377000,31,"R+",120,29,"kworker/2:0",18823
-6824712909377000,2,21000,6824712909398000,79,"S",100,64,"kworker/u17:2",14944
-6824712909398000,2,10000,6824712909408000,31,"S",120,29,"kworker/2:0",18823
-6824712909408000,2,164000,6824712909572000,7,"R+",120,5,"adbd",20305
-6824712909572000,2,42000,6824712909614000,79,"S",100,64,"kworker/u17:2",14944
-6824712909614000,2,53000,6824712909667000,7,"R+",120,5,"adbd",20305
-6824712909667000,2,62000,6824712909729000,79,"S",100,64,"kworker/u17:2",14944
-6824712909729000,2,88000,6824712909817000,7,"S",120,5,"adbd",20305
-6824712909817000,2,183000,6824712910000000,4,"S",120,5,"UsbFfs-worker",20308
-6824712910000000,2,101000,6824712910101000,31,"S",120,29,"kworker/2:0",18823
-6824712910101000,2,40000,6824712910141000,4,"R",120,5,"UsbFfs-worker",20308
-6824712910141000,2,36000,6824712910177000,79,"S",100,64,"kworker/u17:2",14944
-6824712910177000,2,35000,6824712910212000,4,"R",120,5,"UsbFfs-worker",20308
-6824712910212000,2,56000,6824712910268000,79,"S",100,64,"kworker/u17:2",14944
-6824712910268000,2,37000,6824712910305000,31,"R",120,29,"kworker/2:0",18823
-6824712910305000,2,35000,6824712910340000,79,"S",100,64,"kworker/u17:2",14944
-6824712910340000,2,34000,6824712910374000,31,"R",120,29,"kworker/2:0",18823
-6824712910374000,2,46000,6824712910420000,79,"S",100,64,"kworker/u17:2",14944
-6824712910420000,2,78000,6824712910498000,31,"S",120,29,"kworker/2:0",18823
-6824712910498000,2,292000,6824712910790000,4,"S",120,5,"UsbFfs-worker",20308
-6824712910790000,2,280000,6824712911070000,7,"R+",120,5,"adbd",20305
-6824712911070000,2,92000,6824712911162000,79,"S",100,64,"kworker/u17:2",14944
-6824712911162000,2,10000,6824712911172000,2,"S",100,2,"kworker/u17:1",1134
-6824712911172000,2,47000,6824712911219000,7,"R",120,5,"adbd",20305
-6824712911219000,2,22000,6824712911241000,2,"S",100,2,"kworker/u17:1",1134
-6824712911241000,2,85000,6824712911326000,7,"S",120,5,"adbd",20305
-6824712911326000,2,112000,6824712911438000,78,"S",120,5,"shell",20458
-6824712911438000,2,42000,6824712911480000,31,"S",120,29,"kworker/2:0",18823
-6824712911480000,2,22000,6824712911502000,4,"S",120,5,"UsbFfs-worker",20308
-6824712911502000,2,2059000,6824712913561000,75,"S",120,61,"sh",20457
-6824712913561000,2,431000,6824712913992000,80,"R+",120,65,"sh",20459
-6824712913933000,0,84000,6824712914017000,6,"S",120,4,"rcu_preempt",7
-6824712913992000,2,65000,6824712914057000,23,"S",120,21,"rcuop/2",28
+6824712907646000,2,198000,6824712907844000,2734,"S",120,739,"shell",20458
+6824712907844000,2,962000,6824712908806000,739,"R+",120,739,"adbd",20305
+6824712908806000,2,188000,6824712908994000,630,"S",100,630,"kworker/u17:1",1134
+6824712908994000,2,81000,6824712909075000,694,"S",120,694,"kworker/2:0",18823
+6824712909075000,2,25000,6824712909100000,670,"S",100,670,"kworker/u17:2",14944
+6824712909100000,2,79000,6824712909179000,2487,"S",120,739,"UsbFfs-worker",20308
+6824712909179000,2,99000,6824712909278000,739,"R+",120,739,"adbd",20305
+6824712909278000,2,50000,6824712909328000,670,"S",100,670,"kworker/u17:2",14944
+6824712909328000,2,49000,6824712909377000,694,"R+",120,694,"kworker/2:0",18823
+6824712909377000,2,21000,6824712909398000,670,"S",100,670,"kworker/u17:2",14944
+6824712909398000,2,10000,6824712909408000,694,"S",120,694,"kworker/2:0",18823
+6824712909408000,2,164000,6824712909572000,739,"R+",120,739,"adbd",20305
+6824712909572000,2,42000,6824712909614000,670,"S",100,670,"kworker/u17:2",14944
+6824712909614000,2,53000,6824712909667000,739,"R+",120,739,"adbd",20305
+6824712909667000,2,62000,6824712909729000,670,"S",100,670,"kworker/u17:2",14944
+6824712909729000,2,88000,6824712909817000,739,"S",120,739,"adbd",20305
+6824712909817000,2,183000,6824712910000000,2487,"S",120,739,"UsbFfs-worker",20308
+6824712910000000,2,101000,6824712910101000,694,"S",120,694,"kworker/2:0",18823
+6824712910101000,2,40000,6824712910141000,2487,"R",120,739,"UsbFfs-worker",20308
+6824712910141000,2,36000,6824712910177000,670,"S",100,670,"kworker/u17:2",14944
+6824712910177000,2,35000,6824712910212000,2487,"R",120,739,"UsbFfs-worker",20308
+6824712910212000,2,56000,6824712910268000,670,"S",100,670,"kworker/u17:2",14944
+6824712910268000,2,37000,6824712910305000,694,"R",120,694,"kworker/2:0",18823
+6824712910305000,2,35000,6824712910340000,670,"S",100,670,"kworker/u17:2",14944
+6824712910340000,2,34000,6824712910374000,694,"R",120,694,"kworker/2:0",18823
+6824712910374000,2,46000,6824712910420000,670,"S",100,670,"kworker/u17:2",14944
+6824712910420000,2,78000,6824712910498000,694,"S",120,694,"kworker/2:0",18823
+6824712910498000,2,292000,6824712910790000,2487,"S",120,739,"UsbFfs-worker",20308
+6824712910790000,2,280000,6824712911070000,739,"R+",120,739,"adbd",20305
+6824712911070000,2,92000,6824712911162000,670,"S",100,670,"kworker/u17:2",14944
+6824712911162000,2,10000,6824712911172000,630,"S",100,630,"kworker/u17:1",1134
+6824712911172000,2,47000,6824712911219000,739,"R",120,739,"adbd",20305
+6824712911219000,2,22000,6824712911241000,630,"S",100,630,"kworker/u17:1",1134
+6824712911241000,2,85000,6824712911326000,739,"S",120,739,"adbd",20305
+6824712911326000,2,112000,6824712911438000,2734,"S",120,739,"shell",20458
+6824712911438000,2,42000,6824712911480000,694,"S",120,694,"kworker/2:0",18823
+6824712911480000,2,22000,6824712911502000,2487,"S",120,739,"UsbFfs-worker",20308
+6824712911502000,2,2059000,6824712913561000,2733,"S",120,761,"sh",20457
+6824712913561000,2,431000,6824712913992000,2735,"R+",120,762,"sh",20459
+6824712913933000,0,84000,6824712914017000,5,"S",120,5,"rcu_preempt",7
+6824712913992000,2,65000,6824712914057000,24,"S",120,24,"rcuop/2",28
 6824712914017000,0,1135000,6824712915152000,0,"R",120,"[NULL]","swapper/5",0
-6824712914057000,2,670000,6824712914727000,80,"R+",120,65,"sh",20459
-6824712914727000,2,46000,6824712914773000,23,"S",120,21,"rcuop/2",28
-6824712914773000,2,1850000,6824712916623000,80,"R+",120,65,"sh",20459
-6824712915152000,0,39000,6824712915191000,6,"S",120,4,"rcu_preempt",7
+6824712914057000,2,670000,6824712914727000,2735,"R+",120,762,"sh",20459
+6824712914727000,2,46000,6824712914773000,24,"S",120,24,"rcuop/2",28
+6824712914773000,2,1850000,6824712916623000,2735,"R+",120,762,"sh",20459
+6824712915152000,0,39000,6824712915191000,5,"S",120,5,"rcu_preempt",7
 6824712915191000,0,2005000,6824712917196000,0,"R",120,"[NULL]","swapper/5",0
-6824712916623000,2,1909000,6824712918532000,75,"x",120,61,"sh",20457
-6824712916779000,3,89000,6824712916868000,20,"S",120,18,"rcuos/2",29
+6824712916623000,2,1909000,6824712918532000,2733,"x",120,761,"sh",20457
+6824712916779000,3,89000,6824712916868000,25,"S",120,25,"rcuos/2",29
 6824712916868000,3,6871000,6824712923739000,0,"R",120,"[NULL]","swapper/5",0
-6824712917196000,0,38000,6824712917234000,5,"S",120,7,"rcu_sched",8
+6824712917196000,0,38000,6824712917234000,6,"S",120,6,"rcu_sched",8
 6824712917234000,0,3105000,6824712920339000,0,"R",120,"[NULL]","swapper/5",0
-6824712918532000,2,938000,6824712919470000,78,"x",120,5,"shell",20458
-6824712919470000,2,1271000,6824712920741000,7,"S",120,5,"adbd",20305
-6824712920339000,0,118000,6824712920457000,2,"S",100,2,"kworker/u17:1",1134
-6824712920457000,0,98000,6824712920555000,35,"S",120,33,"kworker/0:5",20371
-6824712920555000,0,67000,6824712920622000,4,"S",120,5,"UsbFfs-worker",20308
-6824712920622000,0,44000,6824712920666000,2,"S",100,2,"kworker/u17:1",1134
-6824712920666000,0,83000,6824712920749000,6,"S",120,4,"rcu_preempt",7
-6824712920741000,2,90000,6824712920831000,23,"S",120,21,"rcuop/2",28
+6824712918532000,2,938000,6824712919470000,2734,"x",120,739,"shell",20458
+6824712919470000,2,1271000,6824712920741000,739,"S",120,739,"adbd",20305
+6824712920339000,0,118000,6824712920457000,630,"S",100,630,"kworker/u17:1",1134
+6824712920457000,0,98000,6824712920555000,743,"S",120,743,"kworker/0:5",20371
+6824712920555000,0,67000,6824712920622000,2487,"S",120,739,"UsbFfs-worker",20308
+6824712920622000,0,44000,6824712920666000,630,"S",100,630,"kworker/u17:1",1134
+6824712920666000,0,83000,6824712920749000,5,"S",120,5,"rcu_preempt",7
+6824712920741000,2,90000,6824712920831000,24,"S",120,24,"rcuop/2",28
 6824712920749000,0,63000,6824712920812000,0,"R",120,"[NULL]","swapper/5",0
-6824712920812000,0,59000,6824712920871000,2,"S",100,2,"kworker/u17:1",1134
-6824712920831000,2,100000,6824712920931000,80,"R+",120,65,"sh",20459
-6824712920871000,0,56000,6824712920927000,35,"R+",120,33,"kworker/0:5",20371
-6824712920927000,0,28000,6824712920955000,2,"S",100,2,"kworker/u17:1",1134
-6824712920931000,2,327000,6824712921258000,26,"S",49,23,"sugov:0",605
-6824712920955000,0,8000,6824712920963000,35,"S",120,33,"kworker/0:5",20371
-6824712920963000,0,23000,6824712920986000,4,"R",120,5,"UsbFfs-worker",20308
-6824712920986000,0,40000,6824712921026000,2,"S",100,2,"kworker/u17:1",1134
-6824712921026000,0,41000,6824712921067000,4,"S",120,5,"UsbFfs-worker",20308
-6824712921067000,0,15000,6824712921082000,6,"R+",120,4,"rcu_preempt",7
-6824712921082000,0,68000,6824712921150000,2,"S",100,2,"kworker/u17:1",1134
-6824712921150000,0,50000,6824712921200000,35,"R+",120,33,"kworker/0:5",20371
-6824712921200000,0,48000,6824712921248000,2,"S",100,2,"kworker/u17:1",1134
-6824712921248000,0,48000,6824712921296000,35,"R+",120,33,"kworker/0:5",20371
-6824712921258000,2,54000,6824712921312000,80,"x",120,65,"sh",20459
-6824712921296000,0,38000,6824712921334000,2,"S",100,2,"kworker/u17:1",1134
+6824712920812000,0,59000,6824712920871000,630,"S",100,630,"kworker/u17:1",1134
+6824712920831000,2,100000,6824712920931000,2735,"R+",120,762,"sh",20459
+6824712920871000,0,56000,6824712920927000,743,"R+",120,743,"kworker/0:5",20371
+6824712920927000,0,28000,6824712920955000,630,"S",100,630,"kworker/u17:1",1134
+6824712920931000,2,327000,6824712921258000,482,"S",49,482,"sugov:0",605
+6824712920955000,0,8000,6824712920963000,743,"S",120,743,"kworker/0:5",20371
+6824712920963000,0,23000,6824712920986000,2487,"R",120,739,"UsbFfs-worker",20308
+6824712920986000,0,40000,6824712921026000,630,"S",100,630,"kworker/u17:1",1134
+6824712921026000,0,41000,6824712921067000,2487,"S",120,739,"UsbFfs-worker",20308
+6824712921067000,0,15000,6824712921082000,5,"R+",120,5,"rcu_preempt",7
+6824712921082000,0,68000,6824712921150000,630,"S",100,630,"kworker/u17:1",1134
+6824712921150000,0,50000,6824712921200000,743,"R+",120,743,"kworker/0:5",20371
+6824712921200000,0,48000,6824712921248000,630,"S",100,630,"kworker/u17:1",1134
+6824712921248000,0,48000,6824712921296000,743,"R+",120,743,"kworker/0:5",20371
+6824712921258000,2,54000,6824712921312000,2735,"x",120,762,"sh",20459
+6824712921296000,0,38000,6824712921334000,630,"S",100,630,"kworker/u17:1",1134
 6824712921312000,2,8350000,6824712929662000,0,"R",120,"[NULL]","swapper/5",0
-6824712921334000,0,41000,6824712921375000,35,"S",120,33,"kworker/0:5",20371
-6824712921375000,0,204000,6824712921579000,4,"S",120,5,"UsbFfs-worker",20308
-6824712921579000,0,177000,6824712921756000,7,"S",120,5,"adbd",20305
-6824712921756000,0,15000,6824712921771000,6,"S",120,4,"rcu_preempt",7
+6824712921334000,0,41000,6824712921375000,743,"S",120,743,"kworker/0:5",20371
+6824712921375000,0,204000,6824712921579000,2487,"S",120,739,"UsbFfs-worker",20308
+6824712921579000,0,177000,6824712921756000,739,"S",120,739,"adbd",20305
+6824712921756000,0,15000,6824712921771000,5,"S",120,5,"rcu_preempt",7
 6824712921771000,0,337000,6824712922108000,0,"R",120,"[NULL]","swapper/5",0
-6824712922108000,0,39000,6824712922147000,2,"S",100,2,"kworker/u17:1",1134
-6824712922147000,0,31000,6824712922178000,35,"S",120,33,"kworker/0:5",20371
-6824712922178000,0,23000,6824712922201000,4,"S",120,5,"UsbFfs-worker",20308
+6824712922108000,0,39000,6824712922147000,630,"S",100,630,"kworker/u17:1",1134
+6824712922147000,0,31000,6824712922178000,743,"S",120,743,"kworker/0:5",20371
+6824712922178000,0,23000,6824712922201000,2487,"S",120,739,"UsbFfs-worker",20308
 6824712922201000,0,1099000,6824712923300000,0,"R",120,"[NULL]","swapper/5",0
-6824712923300000,0,66000,6824712923366000,5,"S",120,7,"rcu_sched",8
+6824712923300000,0,66000,6824712923366000,6,"S",120,6,"rcu_sched",8
 6824712923366000,0,2472000,6824712925838000,0,"R",120,"[NULL]","swapper/5",0
-6824712923739000,3,108000,6824712923847000,20,"S",120,18,"rcuos/2",29
-6824712923847000,3,21000,6824712923868000,5,"S",120,7,"rcu_sched",8
+6824712923739000,3,108000,6824712923847000,25,"S",120,25,"rcuos/2",29
+6824712923847000,3,21000,6824712923868000,6,"S",120,6,"rcu_sched",8
 6824712923868000,3,6431000,6824712930299000,0,"R",120,"[NULL]","swapper/5",0
-6824712925838000,0,33000,6824712925871000,35,"R+",120,33,"kworker/0:5",20371
-6824712925871000,0,645000,6824712926516000,36,"S",111,34,"SDM_EventThread",685
-6824712926516000,0,30000,6824712926546000,35,"S",120,33,"kworker/0:5",20371
+6824712925838000,0,33000,6824712925871000,743,"R+",120,743,"kworker/0:5",20371
+6824712925871000,0,645000,6824712926516000,786,"S",111,494,"SDM_EventThread",685
+6824712926516000,0,30000,6824712926546000,743,"S",120,743,"kworker/0:5",20371
 6824712926546000,0,967000,6824712927513000,0,"R",120,"[NULL]","swapper/5",0
-6824712926764000,1,82000,6824712926846000,16,"S",120,14,"kworker/u16:7",19422
-6824712926846000,1,16000,6824712926862000,6,"S",120,4,"rcu_preempt",7
-6824712926862000,1,489000,6824712927351000,38,"S",120,35,"HwBinder:640_1",721
+6824712926764000,1,82000,6824712926846000,702,"S",120,702,"kworker/u16:7",19422
+6824712926846000,1,16000,6824712926862000,5,"S",120,5,"rcu_preempt",7
+6824712926862000,1,489000,6824712927351000,777,"S",120,493,"HwBinder:640_1",721
 6824712927351000,1,1624000,6824712928975000,0,"R",120,"[NULL]","swapper/5",0
-6824712927513000,0,89000,6824712927602000,40,"S",97,35,"DispSync",676
+6824712927513000,0,89000,6824712927602000,771,"S",97,493,"DispSync",676
 6824712927602000,0,969000,6824712928571000,0,"R",120,"[NULL]","swapper/5",0
-6824712928571000,0,92000,6824712928663000,40,"S",97,35,"DispSync",676
+6824712928571000,0,92000,6824712928663000,771,"S",97,493,"DispSync",676
 6824712928663000,0,909000,6824712929572000,0,"R",120,"[NULL]","swapper/5",0
-6824712928975000,1,295000,6824712929270000,41,"S",97,35,"app",678
+6824712928975000,1,295000,6824712929270000,773,"S",97,493,"app",678
 6824712929270000,1,2298000,6824712931568000,0,"R",120,"[NULL]","swapper/5",0
-6824712929572000,0,1853000,6824712931425000,42,"S",120,36,"ndroid.systemui",1664
-6824712929662000,2,29000,6824712929691000,40,"S",97,35,"DispSync",676
+6824712929572000,0,1853000,6824712931425000,644,"S",120,644,"ndroid.systemui",1664
+6824712929662000,2,29000,6824712929691000,771,"S",97,493,"DispSync",676
 6824712929691000,2,2432000,6824712932123000,0,"R",120,"[NULL]","swapper/5",0
-6824712930299000,3,39000,6824712930338000,5,"S",120,7,"rcu_sched",8
-6824712930338000,3,37000,6824712930375000,20,"S",120,18,"rcuos/2",29
+6824712930299000,3,39000,6824712930338000,6,"S",120,6,"rcu_sched",8
+6824712930338000,3,37000,6824712930375000,25,"S",120,25,"rcuos/2",29
 6824712930375000,3,34073000,6824712964448000,0,"R",120,"[NULL]","swapper/5",0
 6824712931425000,0,319000,6824712931744000,0,"R",120,"[NULL]","swapper/5",0
-6824712931568000,1,223000,6824712931791000,43,"S",120,35,"Binder:640_2",675
-6824712931744000,0,102000,6824712931846000,41,"S",97,35,"app",678
+6824712931568000,1,223000,6824712931791000,770,"S",120,493,"Binder:640_2",675
+6824712931744000,0,102000,6824712931846000,773,"S",97,493,"app",678
 6824712931791000,1,1518000,6824712933309000,0,"R",120,"[NULL]","swapper/5",0
 6824712931846000,0,29956000,6824712961802000,0,"R",120,"[NULL]","swapper/5",0
-6824712932123000,2,33000,6824712932156000,40,"S",97,35,"DispSync",676
+6824712932123000,2,33000,6824712932156000,771,"S",97,493,"DispSync",676
 6824712932156000,2,1529000,6824712933685000,0,"R",120,"[NULL]","swapper/5",0
-6824712933309000,1,55000,6824712933364000,6,"S",120,4,"rcu_preempt",7
+6824712933309000,1,55000,6824712933364000,5,"S",120,5,"rcu_preempt",7
 6824712933364000,1,935000,6824712934299000,0,"R",120,"[NULL]","swapper/5",0
-6824712933685000,2,248000,6824712933933000,23,"S",120,21,"rcuop/2",28
+6824712933685000,2,248000,6824712933933000,24,"S",120,24,"rcuop/2",28
 6824712933933000,2,27866000,6824712961799000,0,"R",120,"[NULL]","swapper/5",0
-6824712934299000,1,19000,6824712934318000,6,"S",120,4,"rcu_preempt",7
+6824712934299000,1,19000,6824712934318000,5,"S",120,5,"rcu_preempt",7
 6824712934318000,1,7968000,6824712942286000,0,"R",120,"[NULL]","swapper/5",0
-6824712942286000,1,302000,6824712942588000,26,"S",49,23,"sugov:0",605
-6824712942588000,1,197000,6824712942785000,6,"S",120,4,"rcu_preempt",7
-6824712942785000,1,171000,6824712942956000,23,"S",120,21,"rcuop/2",28
+6824712942286000,1,302000,6824712942588000,482,"S",49,482,"sugov:0",605
+6824712942588000,1,197000,6824712942785000,5,"S",120,5,"rcu_preempt",7
+6824712942785000,1,171000,6824712942956000,24,"S",120,24,"rcuop/2",28
 6824712942956000,1,19657000,6824712962613000,0,"R",120,"[NULL]","swapper/5",0
-6824712961799000,2,620000,6824712962419000,25,"S",120,24,"ksoftirqd/2",25
-6824712961802000,0,176000,6824712961978000,35,"S",120,33,"kworker/0:5",20371
-6824712961978000,0,108000,6824712962086000,2,"S",100,2,"kworker/u17:1",1134
-6824712962086000,0,239000,6824712962325000,36,"R+",111,34,"SDM_EventThread",685
-6824712962325000,0,174000,6824712962499000,2,"S",100,2,"kworker/u17:1",1134
-6824712962419000,2,165000,6824712962584000,31,"S",120,29,"kworker/2:0",18823
-6824712962499000,0,997000,6824712963496000,16,"S",120,14,"kworker/u16:7",19422
+6824712961799000,2,620000,6824712962419000,22,"S",120,22,"ksoftirqd/2",25
+6824712961802000,0,176000,6824712961978000,743,"S",120,743,"kworker/0:5",20371
+6824712961978000,0,108000,6824712962086000,630,"S",100,630,"kworker/u17:1",1134
+6824712962086000,0,239000,6824712962325000,786,"R+",111,494,"SDM_EventThread",685
+6824712962325000,0,174000,6824712962499000,630,"S",100,630,"kworker/u17:1",1134
+6824712962419000,2,165000,6824712962584000,694,"S",120,694,"kworker/2:0",18823
+6824712962499000,0,997000,6824712963496000,702,"S",120,702,"kworker/u16:7",19422
 6824712962584000,2,967000,6824712963551000,0,"R",120,"[NULL]","swapper/5",0
-6824712962613000,1,435000,6824712963048000,40,"S",97,35,"DispSync",676
-6824712963048000,1,140000,6824712963188000,2,"S",100,2,"kworker/u17:1",1134
-6824712963188000,1,505000,6824712963693000,48,"S",120,41,"kworker/1:1",18800
-6824712963496000,0,688000,6824712964184000,36,"S",111,34,"SDM_EventThread",685
-6824712963551000,2,518000,6824712964069000,41,"S",97,35,"app",678
-6824712963693000,1,156000,6824712963849000,4,"S",120,5,"UsbFfs-worker",20308
+6824712962613000,1,435000,6824712963048000,771,"S",97,493,"DispSync",676
+6824712963048000,1,140000,6824712963188000,630,"S",100,630,"kworker/u17:1",1134
+6824712963188000,1,505000,6824712963693000,693,"S",120,693,"kworker/1:1",18800
+6824712963496000,0,688000,6824712964184000,786,"S",111,494,"SDM_EventThread",685
+6824712963551000,2,518000,6824712964069000,773,"S",97,493,"app",678
+6824712963693000,1,156000,6824712963849000,2487,"S",120,739,"UsbFfs-worker",20308
 6824712963849000,1,66000,6824712963915000,0,"R",120,"[NULL]","swapper/5",0
-6824712963915000,1,403000,6824712964318000,42,"R",120,36,"ndroid.systemui",1664
+6824712963915000,1,403000,6824712964318000,644,"R",120,644,"ndroid.systemui",1664
 6824712964069000,2,11256000,6824712975325000,0,"R",120,"[NULL]","swapper/5",0
-6824712964184000,0,198000,6824712964382000,35,"S",120,33,"kworker/0:5",20371
-6824712964318000,1,572000,6824712964890000,4,"S",120,5,"UsbFfs-worker",20308
+6824712964184000,0,198000,6824712964382000,743,"S",120,743,"kworker/0:5",20371
+6824712964318000,1,572000,6824712964890000,2487,"S",120,739,"UsbFfs-worker",20308
 6824712964382000,0,882000,6824712965264000,0,"R",120,"[NULL]","swapper/5",0
-6824712964448000,3,67000,6824712964515000,40,"S",97,35,"DispSync",676
-6824712964515000,3,578000,6824712965093000,38,"S",120,35,"HwBinder:640_1",721
-6824712964890000,1,6507000,6824712971397000,7,"S",120,5,"adbd",20305
+6824712964448000,3,67000,6824712964515000,771,"S",97,493,"DispSync",676
+6824712964515000,3,578000,6824712965093000,777,"S",120,493,"HwBinder:640_1",721
+6824712964890000,1,6507000,6824712971397000,739,"S",120,739,"adbd",20305
 6824712965093000,3,29801000,6824712994894000,0,"R",120,"[NULL]","swapper/5",0
-6824712965264000,0,98000,6824712965362000,40,"S",97,35,"DispSync",676
+6824712965264000,0,98000,6824712965362000,771,"S",97,493,"DispSync",676
 6824712965362000,0,9149000,6824712974511000,0,"R",120,"[NULL]","swapper/5",0
-6824712971397000,1,2878000,6824712974275000,42,"S",120,36,"ndroid.systemui",1664
-6824712974275000,1,2508000,6824712976783000,81,"R+",120,66,"sh",20460
-6824712974511000,0,515000,6824712975026000,43,"S",120,35,"Binder:640_2",675
+6824712971397000,1,2878000,6824712974275000,644,"S",120,644,"ndroid.systemui",1664
+6824712974275000,1,2508000,6824712976783000,2720,"R+",120,755,"sh",20460
+6824712974511000,0,515000,6824712975026000,770,"S",120,493,"Binder:640_2",675
 6824712975026000,0,904000,6824712975930000,0,"R",120,"[NULL]","swapper/5",0
-6824712975325000,2,324000,6824712975649000,41,"S",97,35,"app",678
+6824712975325000,2,324000,6824712975649000,773,"S",97,493,"app",678
 6824712975649000,2,11143000,6824712986792000,0,"R",120,"[NULL]","swapper/5",0
-6824712975930000,0,113000,6824712976043000,40,"S",97,35,"DispSync",676
+6824712975930000,0,113000,6824712976043000,771,"S",97,493,"DispSync",676
 6824712976043000,0,5748000,6824712981791000,0,"R",120,"[NULL]","swapper/5",0
-6824712976783000,1,140000,6824712976923000,16,"S",120,14,"kworker/u16:7",19422
-6824712976923000,1,2388000,6824712979311000,81,"R+",120,66,"sh",20460
-6824712979311000,1,127000,6824712979438000,14,"S",120,10,"rcuos/0",11
-6824712979438000,1,54000,6824712979492000,5,"S",120,7,"rcu_sched",8
-6824712979492000,1,1949000,6824712981441000,81,"R+",120,66,"sh",20460
-6824712981441000,1,88000,6824712981529000,11,"S",120,9,"rcuop/0",10
-6824712981529000,1,40000,6824712981569000,6,"S",120,4,"rcu_preempt",7
-6824712981569000,1,1127000,6824712982696000,81,"R+",120,66,"sh",20460
-6824712981791000,0,2438000,6824712984229000,7,"S",120,5,"adbd",20305
-6824712982493000,7,74000,6824712982567000,76,"S",120,62,"hwrng",215
+6824712976783000,1,140000,6824712976923000,702,"S",120,702,"kworker/u16:7",19422
+6824712976923000,1,2388000,6824712979311000,2720,"R+",120,755,"sh",20460
+6824712979311000,1,127000,6824712979438000,9,"S",120,9,"rcuos/0",11
+6824712979438000,1,54000,6824712979492000,6,"S",120,6,"rcu_sched",8
+6824712979492000,1,1949000,6824712981441000,2720,"R+",120,755,"sh",20460
+6824712981441000,1,88000,6824712981529000,8,"S",120,8,"rcuop/0",10
+6824712981529000,1,40000,6824712981569000,5,"S",120,5,"rcu_preempt",7
+6824712981569000,1,1127000,6824712982696000,2720,"R+",120,755,"sh",20460
+6824712981791000,0,2438000,6824712984229000,739,"S",120,739,"adbd",20305
+6824712982493000,7,74000,6824712982567000,145,"S",120,145,"hwrng",215
 6824712982567000,7,268581000,6824713251148000,0,"R",120,"[NULL]","swapper/5",0
-6824712982696000,1,31000,6824712982727000,76,"S",120,62,"hwrng",215
-6824712982727000,1,630000,6824712983357000,81,"R",120,66,"sh",20460
-6824712983357000,1,58000,6824712983415000,5,"S",120,7,"rcu_sched",8
-6824712983415000,1,820000,6824712984235000,81,"R+",120,66,"sh",20460
-6824712984229000,0,571000,6824712984800000,82,"S",120,5,"shell",20461
-6824712984235000,1,175000,6824712984410000,2,"S",100,2,"kworker/u17:1",1134
-6824712984410000,1,138000,6824712984548000,48,"S",120,41,"kworker/1:1",18800
-6824712984548000,1,27000,6824712984575000,76,"S",120,62,"hwrng",215
-6824712984575000,1,155000,6824712984730000,4,"S",120,5,"UsbFfs-worker",20308
-6824712984730000,1,290000,6824712985020000,81,"R+",120,66,"sh",20460
+6824712982696000,1,31000,6824712982727000,145,"S",120,145,"hwrng",215
+6824712982727000,1,630000,6824712983357000,2720,"R",120,755,"sh",20460
+6824712983357000,1,58000,6824712983415000,6,"S",120,6,"rcu_sched",8
+6824712983415000,1,820000,6824712984235000,2720,"R+",120,755,"sh",20460
+6824712984229000,0,571000,6824712984800000,2728,"S",120,739,"shell",20461
+6824712984235000,1,175000,6824712984410000,630,"S",100,630,"kworker/u17:1",1134
+6824712984410000,1,138000,6824712984548000,693,"S",120,693,"kworker/1:1",18800
+6824712984548000,1,27000,6824712984575000,145,"S",120,145,"hwrng",215
+6824712984575000,1,155000,6824712984730000,2487,"S",120,739,"UsbFfs-worker",20308
+6824712984730000,1,290000,6824712985020000,2720,"R+",120,755,"sh",20460
 6824712984800000,0,3033000,6824712987833000,0,"R",120,"[NULL]","swapper/5",0
-6824712985020000,1,73000,6824712985093000,2,"S",100,2,"kworker/u17:1",1134
-6824712985093000,1,600000,6824712985693000,81,"R+",120,66,"sh",20460
-6824712985693000,1,141000,6824712985834000,2,"S",100,2,"kworker/u17:1",1134
-6824712985834000,1,51000,6824712985885000,48,"R+",120,41,"kworker/1:1",18800
-6824712985885000,1,79000,6824712985964000,2,"S",100,2,"kworker/u17:1",1134
-6824712985964000,1,186000,6824712986150000,48,"S",120,41,"kworker/1:1",18800
-6824712986150000,1,639000,6824712986789000,4,"R+",120,5,"UsbFfs-worker",20308
-6824712986789000,1,238000,6824712987027000,26,"S",49,23,"sugov:0",605
-6824712986792000,2,82000,6824712986874000,6,"S",120,4,"rcu_preempt",7
+6824712985020000,1,73000,6824712985093000,630,"S",100,630,"kworker/u17:1",1134
+6824712985093000,1,600000,6824712985693000,2720,"R+",120,755,"sh",20460
+6824712985693000,1,141000,6824712985834000,630,"S",100,630,"kworker/u17:1",1134
+6824712985834000,1,51000,6824712985885000,693,"R+",120,693,"kworker/1:1",18800
+6824712985885000,1,79000,6824712985964000,630,"S",100,630,"kworker/u17:1",1134
+6824712985964000,1,186000,6824712986150000,693,"S",120,693,"kworker/1:1",18800
+6824712986150000,1,639000,6824712986789000,2487,"R+",120,739,"UsbFfs-worker",20308
+6824712986789000,1,238000,6824712987027000,482,"S",49,482,"sugov:0",605
+6824712986792000,2,82000,6824712986874000,5,"S",120,5,"rcu_preempt",7
 6824712986874000,2,6897000,6824712993771000,0,"R",120,"[NULL]","swapper/5",0
-6824712987027000,1,358000,6824712987385000,7,"S",120,5,"adbd",20305
-6824712987385000,1,192000,6824712987577000,4,"S",120,5,"UsbFfs-worker",20308
-6824712987577000,1,951000,6824712988528000,81,"R+",120,66,"sh",20460
-6824712987833000,0,421000,6824712988254000,7,"S",120,5,"adbd",20305
-6824712988254000,0,185000,6824712988439000,82,"S",120,5,"shell",20461
+6824712987027000,1,358000,6824712987385000,739,"S",120,739,"adbd",20305
+6824712987385000,1,192000,6824712987577000,2487,"S",120,739,"UsbFfs-worker",20308
+6824712987577000,1,951000,6824712988528000,2720,"R+",120,755,"sh",20460
+6824712987833000,0,421000,6824712988254000,739,"S",120,739,"adbd",20305
+6824712988254000,0,185000,6824712988439000,2728,"S",120,739,"shell",20461
 6824712988439000,0,4499000,6824712992938000,0,"R",120,"[NULL]","swapper/5",0
-6824712988528000,1,87000,6824712988615000,2,"S",100,2,"kworker/u17:1",1134
-6824712988615000,1,69000,6824712988684000,48,"S",120,41,"kworker/1:1",18800
-6824712988684000,1,65000,6824712988749000,4,"S",120,5,"UsbFfs-worker",20308
-6824712988749000,1,1248000,6824712989997000,81,"R",120,66,"sh",20460
-6824712989997000,1,90000,6824712990087000,5,"S",120,7,"rcu_sched",8
-6824712990087000,1,92000,6824712990179000,14,"S",120,10,"rcuos/0",11
-6824712990179000,1,76000,6824712990255000,15,"S",120,13,"rcuos/1",21
-6824712990255000,1,24000,6824712990279000,5,"S",120,7,"rcu_sched",8
-6824712990279000,1,2760000,6824712993039000,81,"R+",120,66,"sh",20460
-6824712992938000,0,152000,6824712993090000,35,"S",120,33,"kworker/0:5",20371
-6824712993039000,1,804000,6824712993843000,36,"S",111,34,"SDM_EventThread",685
+6824712988528000,1,87000,6824712988615000,630,"S",100,630,"kworker/u17:1",1134
+6824712988615000,1,69000,6824712988684000,693,"S",120,693,"kworker/1:1",18800
+6824712988684000,1,65000,6824712988749000,2487,"S",120,739,"UsbFfs-worker",20308
+6824712988749000,1,1248000,6824712989997000,2720,"R",120,755,"sh",20460
+6824712989997000,1,90000,6824712990087000,6,"S",120,6,"rcu_sched",8
+6824712990087000,1,92000,6824712990179000,9,"S",120,9,"rcuos/0",11
+6824712990179000,1,76000,6824712990255000,18,"S",120,18,"rcuos/1",21
+6824712990255000,1,24000,6824712990279000,6,"S",120,6,"rcu_sched",8
+6824712990279000,1,2760000,6824712993039000,2720,"R+",120,755,"sh",20460
+6824712992938000,0,152000,6824712993090000,743,"S",120,743,"kworker/0:5",20371
+6824712993039000,1,804000,6824712993843000,786,"S",111,494,"SDM_EventThread",685
 6824712993090000,0,998000,6824712994088000,0,"R",120,"[NULL]","swapper/5",0
-6824712993771000,2,89000,6824712993860000,6,"S",120,4,"rcu_preempt",7
-6824712993843000,1,145000,6824712993988000,11,"S",120,9,"rcuop/0",10
+6824712993771000,2,89000,6824712993860000,5,"S",120,5,"rcu_preempt",7
+6824712993843000,1,145000,6824712993988000,8,"S",120,8,"rcuop/0",10
 6824712993860000,2,587000,6824712994447000,0,"R",120,"[NULL]","swapper/5",0
-6824712993988000,1,34000,6824712994022000,13,"S",120,12,"rcuop/1",20
-6824712994022000,1,2637000,6824712996659000,81,"R",120,66,"sh",20460
-6824712994088000,0,576000,6824712994664000,38,"S",120,35,"HwBinder:640_1",721
-6824712994447000,2,41000,6824712994488000,6,"S",120,4,"rcu_preempt",7
+6824712993988000,1,34000,6824712994022000,17,"S",120,17,"rcuop/1",20
+6824712994022000,1,2637000,6824712996659000,2720,"R",120,755,"sh",20460
+6824712994088000,0,576000,6824712994664000,777,"S",120,493,"HwBinder:640_1",721
+6824712994447000,2,41000,6824712994488000,5,"S",120,5,"rcu_preempt",7
 6824712994488000,2,1596000,6824712996084000,0,"R",120,"[NULL]","swapper/5",0
 6824712994664000,0,752000,6824712995416000,0,"R",120,"[NULL]","swapper/5",0
-6824712994894000,3,230000,6824712995124000,40,"S",97,35,"DispSync",676
+6824712994894000,3,230000,6824712995124000,771,"S",97,493,"DispSync",676
 6824712995124000,3,1089000,6824712996213000,0,"R",120,"[NULL]","swapper/5",0
-6824712995416000,0,388000,6824712995804000,41,"S",97,35,"app",678
+6824712995416000,0,388000,6824712995804000,773,"S",97,493,"app",678
 6824712995804000,0,2854000,6824712998658000,0,"R",120,"[NULL]","swapper/5",0
-6824712996084000,2,2507000,6824712998591000,42,"S",120,36,"ndroid.systemui",1664
-6824712996213000,3,61000,6824712996274000,40,"S",97,35,"DispSync",676
+6824712996084000,2,2507000,6824712998591000,644,"S",120,644,"ndroid.systemui",1664
+6824712996213000,3,61000,6824712996274000,771,"S",97,493,"DispSync",676
 6824712996274000,3,31193000,6824713027467000,0,"R",120,"[NULL]","swapper/5",0
-6824712996659000,1,76000,6824712996735000,5,"S",120,7,"rcu_sched",8
-6824712996735000,1,52000,6824712996787000,14,"S",120,10,"rcuos/0",11
-6824712996787000,1,28000,6824712996815000,15,"S",120,13,"rcuos/1",21
-6824712996815000,1,3192000,6824713000007000,81,"R+",120,66,"sh",20460
+6824712996659000,1,76000,6824712996735000,6,"S",120,6,"rcu_sched",8
+6824712996735000,1,52000,6824712996787000,9,"S",120,9,"rcuos/0",11
+6824712996787000,1,28000,6824712996815000,18,"S",120,18,"rcuos/1",21
+6824712996815000,1,3192000,6824713000007000,2720,"R+",120,755,"sh",20460
 6824712998591000,2,346000,6824712998937000,0,"R",120,"[NULL]","swapper/5",0
-6824712998658000,0,379000,6824712999037000,43,"S",120,35,"Binder:640_2",675
-6824712998937000,2,202000,6824712999139000,41,"S",97,35,"app",678
+6824712998658000,0,379000,6824712999037000,770,"S",120,493,"Binder:640_2",675
+6824712998937000,2,202000,6824712999139000,773,"S",97,493,"app",678
 6824712999037000,0,34000,6824712999071000,0,"R",120,"[NULL]","swapper/5",0
-6824712999071000,0,62000,6824712999133000,40,"S",97,35,"DispSync",676
+6824712999071000,0,62000,6824712999133000,771,"S",97,493,"DispSync",676
 6824712999133000,0,22793000,6824713021926000,0,"R",120,"[NULL]","swapper/5",0
 6824712999139000,2,28073000,6824713027212000,0,"R",120,"[NULL]","swapper/5",0
-6824713000007000,1,107000,6824713000114000,6,"S",120,4,"rcu_preempt",7
-6824713000114000,1,92000,6824713000206000,11,"S",120,9,"rcuop/0",10
-6824713000206000,1,61000,6824713000267000,13,"S",120,12,"rcuop/1",20
-6824713000267000,1,3074000,6824713003341000,81,"R",120,66,"sh",20460
-6824713003341000,1,209000,6824713003550000,26,"S",49,23,"sugov:0",605
-6824713003550000,1,7239000,6824713010789000,81,"R",120,66,"sh",20460
-6824713010789000,1,52000,6824713010841000,11,"S",120,9,"rcuop/0",10
-6824713010841000,1,35000,6824713010876000,6,"S",120,4,"rcu_preempt",7
-6824713010876000,1,95000,6824713010971000,81,"R+",120,66,"sh",20460
-6824713010971000,1,26000,6824713010997000,76,"S",120,62,"hwrng",215
-6824713010997000,1,5636000,6824713016633000,81,"R",120,66,"sh",20460
-6824713016633000,1,67000,6824713016700000,6,"S",120,4,"rcu_preempt",7
-6824713016700000,1,66000,6824713016766000,11,"S",120,9,"rcuop/0",10
-6824713016766000,1,36000,6824713016802000,13,"S",120,12,"rcuop/1",20
-6824713016802000,1,12000,6824713016814000,6,"S",120,4,"rcu_preempt",7
-6824713016814000,1,1724000,6824713018538000,81,"S",120,66,"sh",20460
-6824713018538000,1,2161000,6824713020699000,83,"R+",120,67,"sh",20462
-6824713020699000,1,225000,6824713020924000,26,"S",49,23,"sugov:0",605
-6824713020924000,1,273000,6824713021197000,83,"S",120,67,"sh",20462
+6824713000007000,1,107000,6824713000114000,5,"S",120,5,"rcu_preempt",7
+6824713000114000,1,92000,6824713000206000,8,"S",120,8,"rcuop/0",10
+6824713000206000,1,61000,6824713000267000,17,"S",120,17,"rcuop/1",20
+6824713000267000,1,3074000,6824713003341000,2720,"R",120,755,"sh",20460
+6824713003341000,1,209000,6824713003550000,482,"S",49,482,"sugov:0",605
+6824713003550000,1,7239000,6824713010789000,2720,"R",120,755,"sh",20460
+6824713010789000,1,52000,6824713010841000,8,"S",120,8,"rcuop/0",10
+6824713010841000,1,35000,6824713010876000,5,"S",120,5,"rcu_preempt",7
+6824713010876000,1,95000,6824713010971000,2720,"R+",120,755,"sh",20460
+6824713010971000,1,26000,6824713010997000,145,"S",120,145,"hwrng",215
+6824713010997000,1,5636000,6824713016633000,2720,"R",120,755,"sh",20460
+6824713016633000,1,67000,6824713016700000,5,"S",120,5,"rcu_preempt",7
+6824713016700000,1,66000,6824713016766000,8,"S",120,8,"rcuop/0",10
+6824713016766000,1,36000,6824713016802000,17,"S",120,17,"rcuop/1",20
+6824713016802000,1,12000,6824713016814000,5,"S",120,5,"rcu_preempt",7
+6824713016814000,1,1724000,6824713018538000,2720,"S",120,755,"sh",20460
+6824713018538000,1,2161000,6824713020699000,2721,"R+",120,756,"sh",20462
+6824713020699000,1,225000,6824713020924000,482,"S",49,482,"sugov:0",605
+6824713020924000,1,273000,6824713021197000,2721,"S",120,756,"sh",20462
 6824713021197000,1,5881000,6824713027078000,0,"R",120,"[NULL]","swapper/5",0
-6824713021926000,0,1729000,6824713023655000,84,"R+",120,68,"ps",20463
-6824713023655000,0,62000,6824713023717000,14,"S",120,10,"rcuos/0",11
-6824713023717000,0,29000,6824713023746000,5,"S",120,7,"rcu_sched",8
-6824713023746000,0,29000,6824713023775000,84,"R+",120,68,"ps",20463
-6824713023775000,0,30000,6824713023805000,6,"S",120,4,"rcu_preempt",7
-6824713023805000,0,413000,6824713024218000,84,"R+",120,68,"ps",20463
-6824713024218000,0,23000,6824713024241000,76,"S",120,62,"hwrng",215
-6824713024241000,0,556000,6824713024797000,84,"R+",120,68,"ps",20463
-6824713024797000,0,9000,6824713024806000,76,"S",120,62,"hwrng",215
-6824713024806000,0,1217000,6824713026023000,84,"R+",120,68,"ps",20463
-6824713026023000,0,91000,6824713026114000,35,"S",120,33,"kworker/0:5",20371
-6824713026114000,0,814000,6824713026928000,36,"S",111,34,"SDM_EventThread",685
-6824713026928000,0,45000,6824713026973000,6,"S",120,4,"rcu_preempt",7
-6824713026973000,0,263000,6824713027236000,84,"R+",120,68,"ps",20463
-6824713027078000,1,93000,6824713027171000,16,"S",120,14,"kworker/u16:7",19422
-6824713027171000,1,67000,6824713027238000,11,"S",120,9,"rcuop/0",10
-6824713027212000,2,447000,6824713027659000,38,"S",120,35,"HwBinder:640_1",721
-6824713027236000,0,14000,6824713027250000,6,"S",120,4,"rcu_preempt",7
-6824713027238000,1,61000,6824713027299000,13,"S",120,12,"rcuop/1",20
-6824713027250000,0,2732000,6824713029982000,84,"R",120,68,"ps",20463
+6824713021926000,0,1729000,6824713023655000,2722,"R+",120,757,"ps",20463
+6824713023655000,0,62000,6824713023717000,9,"S",120,9,"rcuos/0",11
+6824713023717000,0,29000,6824713023746000,6,"S",120,6,"rcu_sched",8
+6824713023746000,0,29000,6824713023775000,2722,"R+",120,757,"ps",20463
+6824713023775000,0,30000,6824713023805000,5,"S",120,5,"rcu_preempt",7
+6824713023805000,0,413000,6824713024218000,2722,"R+",120,757,"ps",20463
+6824713024218000,0,23000,6824713024241000,145,"S",120,145,"hwrng",215
+6824713024241000,0,556000,6824713024797000,2722,"R+",120,757,"ps",20463
+6824713024797000,0,9000,6824713024806000,145,"S",120,145,"hwrng",215
+6824713024806000,0,1217000,6824713026023000,2722,"R+",120,757,"ps",20463
+6824713026023000,0,91000,6824713026114000,743,"S",120,743,"kworker/0:5",20371
+6824713026114000,0,814000,6824713026928000,786,"S",111,494,"SDM_EventThread",685
+6824713026928000,0,45000,6824713026973000,5,"S",120,5,"rcu_preempt",7
+6824713026973000,0,263000,6824713027236000,2722,"R+",120,757,"ps",20463
+6824713027078000,1,93000,6824713027171000,702,"S",120,702,"kworker/u16:7",19422
+6824713027171000,1,67000,6824713027238000,8,"S",120,8,"rcuop/0",10
+6824713027212000,2,447000,6824713027659000,777,"S",120,493,"HwBinder:640_1",721
+6824713027236000,0,14000,6824713027250000,5,"S",120,5,"rcu_preempt",7
+6824713027238000,1,61000,6824713027299000,17,"S",120,17,"rcuop/1",20
+6824713027250000,0,2732000,6824713029982000,2722,"R",120,757,"ps",20463
 6824713027299000,1,655000,6824713027954000,0,"R",120,"[NULL]","swapper/5",0
-6824713027467000,3,179000,6824713027646000,40,"S",97,35,"DispSync",676
+6824713027467000,3,179000,6824713027646000,771,"S",97,493,"DispSync",676
 6824713027646000,3,1029000,6824713028675000,0,"R",120,"[NULL]","swapper/5",0
 6824713027659000,2,901000,6824713028560000,0,"R",120,"[NULL]","swapper/5",0
-6824713027954000,1,314000,6824713028268000,41,"S",97,35,"app",678
+6824713027954000,1,314000,6824713028268000,773,"S",97,493,"app",678
 6824713028268000,1,2352000,6824713030620000,0,"R",120,"[NULL]","swapper/5",0
-6824713028560000,2,1914000,6824713030474000,42,"S",120,36,"ndroid.systemui",1664
-6824713028675000,3,34000,6824713028709000,40,"S",97,35,"DispSync",676
+6824713028560000,2,1914000,6824713030474000,644,"S",120,644,"ndroid.systemui",1664
+6824713028675000,3,34000,6824713028709000,771,"S",97,493,"DispSync",676
 6824713028709000,3,34349000,6824713063058000,0,"R",120,"[NULL]","swapper/5",0
-6824713029982000,0,33000,6824713030015000,5,"S",120,7,"rcu_sched",8
-6824713030015000,0,3308000,6824713033323000,84,"R+",120,68,"ps",20463
+6824713029982000,0,33000,6824713030015000,6,"S",120,6,"rcu_sched",8
+6824713030015000,0,3308000,6824713033323000,2722,"R+",120,757,"ps",20463
 6824713030474000,2,705000,6824713031179000,0,"R",120,"[NULL]","swapper/5",0
-6824713030620000,1,274000,6824713030894000,43,"S",120,35,"Binder:640_2",675
+6824713030620000,1,274000,6824713030894000,770,"S",120,493,"Binder:640_2",675
 6824713030894000,1,696000,6824713031590000,0,"R",120,"[NULL]","swapper/5",0
-6824713031179000,2,137000,6824713031316000,41,"S",97,35,"app",678
+6824713031179000,2,137000,6824713031316000,773,"S",97,493,"app",678
 6824713031316000,2,29313000,6824713060629000,0,"R",120,"[NULL]","swapper/5",0
-6824713031590000,1,43000,6824713031633000,40,"S",97,35,"DispSync",676
+6824713031590000,1,43000,6824713031633000,771,"S",97,493,"DispSync",676
 6824713031633000,1,2153000,6824713033786000,0,"R",120,"[NULL]","swapper/5",0
-6824713033323000,0,89000,6824713033412000,6,"S",120,4,"rcu_preempt",7
-6824713033412000,0,86000,6824713033498000,11,"S",120,9,"rcuop/0",10
-6824713033498000,0,13000,6824713033511000,6,"S",120,4,"rcu_preempt",7
-6824713033511000,0,3137000,6824713036648000,84,"R",120,68,"ps",20463
-6824713033786000,1,36000,6824713033822000,13,"S",120,12,"rcuop/1",20
+6824713033323000,0,89000,6824713033412000,5,"S",120,5,"rcu_preempt",7
+6824713033412000,0,86000,6824713033498000,8,"S",120,8,"rcuop/0",10
+6824713033498000,0,13000,6824713033511000,5,"S",120,5,"rcu_preempt",7
+6824713033511000,0,3137000,6824713036648000,2722,"R",120,757,"ps",20463
+6824713033786000,1,36000,6824713033822000,17,"S",120,17,"rcuop/1",20
 6824713033822000,1,6659000,6824713040481000,0,"R",120,"[NULL]","swapper/5",0
-6824713036648000,0,56000,6824713036704000,5,"S",120,7,"rcu_sched",8
-6824713036704000,0,54000,6824713036758000,14,"S",120,10,"rcuos/0",11
-6824713036758000,0,10000,6824713036768000,5,"S",120,7,"rcu_sched",8
-6824713036768000,0,6593000,6824713043361000,84,"R",120,68,"ps",20463
-6824713040481000,1,46000,6824713040527000,6,"S",120,4,"rcu_preempt",7
+6824713036648000,0,56000,6824713036704000,6,"S",120,6,"rcu_sched",8
+6824713036704000,0,54000,6824713036758000,9,"S",120,9,"rcuos/0",11
+6824713036758000,0,10000,6824713036768000,6,"S",120,6,"rcu_sched",8
+6824713036768000,0,6593000,6824713043361000,2722,"R",120,757,"ps",20463
+6824713040481000,1,46000,6824713040527000,5,"S",120,5,"rcu_preempt",7
 6824713040527000,1,3210000,6824713043737000,0,"R",120,"[NULL]","swapper/5",0
-6824713043361000,0,195000,6824713043556000,26,"S",49,23,"sugov:0",605
-6824713043556000,0,3032000,6824713046588000,84,"R",120,68,"ps",20463
-6824713043737000,1,36000,6824713043773000,5,"S",120,7,"rcu_sched",8
+6824713043361000,0,195000,6824713043556000,482,"S",49,482,"sugov:0",605
+6824713043556000,0,3032000,6824713046588000,2722,"R",120,757,"ps",20463
+6824713043737000,1,36000,6824713043773000,6,"S",120,6,"rcu_sched",8
 6824713043773000,1,3242000,6824713047015000,0,"R",120,"[NULL]","swapper/5",0
-6824713046588000,0,37000,6824713046625000,26,"S",49,23,"sugov:0",605
-6824713046625000,0,431000,6824713047056000,84,"R",120,68,"ps",20463
-6824713047015000,1,48000,6824713047063000,6,"S",120,4,"rcu_preempt",7
-6824713047056000,0,43000,6824713047099000,11,"S",120,9,"rcuop/0",10
+6824713046588000,0,37000,6824713046625000,482,"S",49,482,"sugov:0",605
+6824713046625000,0,431000,6824713047056000,2722,"R",120,757,"ps",20463
+6824713047015000,1,48000,6824713047063000,5,"S",120,5,"rcu_preempt",7
+6824713047056000,0,43000,6824713047099000,8,"S",120,8,"rcuop/0",10
 6824713047063000,1,3191000,6824713050254000,0,"R",120,"[NULL]","swapper/5",0
-6824713047099000,0,3188000,6824713050287000,84,"R",120,68,"ps",20463
-6824713050254000,1,42000,6824713050296000,5,"S",120,7,"rcu_sched",8
-6824713050287000,0,36000,6824713050323000,14,"S",120,10,"rcuos/0",11
+6824713047099000,0,3188000,6824713050287000,2722,"R",120,757,"ps",20463
+6824713050254000,1,42000,6824713050296000,6,"S",120,6,"rcu_sched",8
+6824713050287000,0,36000,6824713050323000,9,"S",120,9,"rcuos/0",11
 6824713050296000,1,4103000,6824713054399000,0,"R",120,"[NULL]","swapper/5",0
-6824713050323000,0,3621000,6824713053944000,84,"R",120,68,"ps",20463
-6824713053165000,6,647000,6824713053812000,85,"S",120,69,"m.android.phone",1702
+6824713050323000,0,3621000,6824713053944000,2722,"R",120,757,"ps",20463
+6824713053165000,6,647000,6824713053812000,650,"S",120,650,"m.android.phone",1702
 6824713053812000,6,196550000,6824713250362000,0,"R",120,"[NULL]","swapper/5",0
-6824713053944000,0,46000,6824713053990000,11,"S",120,9,"rcuop/0",10
-6824713053990000,0,65000,6824713054055000,84,"R+",120,68,"ps",20463
-6824713054055000,0,22000,6824713054077000,76,"S",120,62,"hwrng",215
-6824713054077000,0,5450000,6824713059527000,84,"R",120,68,"ps",20463
-6824713054399000,1,30000,6824713054429000,6,"S",120,4,"rcu_preempt",7
+6824713053944000,0,46000,6824713053990000,8,"S",120,8,"rcuop/0",10
+6824713053990000,0,65000,6824713054055000,2722,"R+",120,757,"ps",20463
+6824713054055000,0,22000,6824713054077000,145,"S",120,145,"hwrng",215
+6824713054077000,0,5450000,6824713059527000,2722,"R",120,757,"ps",20463
+6824713054399000,1,30000,6824713054429000,5,"S",120,5,"rcu_preempt",7
 6824713054429000,1,6141000,6824713060570000,0,"R",120,"[NULL]","swapper/5",0
-6824713059527000,0,91000,6824713059618000,35,"S",120,33,"kworker/0:5",20371
-6824713059618000,0,735000,6824713060353000,36,"S",111,34,"SDM_EventThread",685
-6824713060353000,0,48000,6824713060401000,35,"S",120,33,"kworker/0:5",20371
-6824713060401000,0,2914000,6824713063315000,84,"R+",120,68,"ps",20463
-6824713060570000,1,23000,6824713060593000,47,"S",120,40,"ksoftirqd/1",17
-6824713060593000,1,54000,6824713060647000,6,"S",120,4,"rcu_preempt",7
-6824713060629000,2,431000,6824713061060000,38,"S",120,35,"HwBinder:640_1",721
+6824713059527000,0,91000,6824713059618000,743,"S",120,743,"kworker/0:5",20371
+6824713059618000,0,735000,6824713060353000,786,"S",111,494,"SDM_EventThread",685
+6824713060353000,0,48000,6824713060401000,743,"S",120,743,"kworker/0:5",20371
+6824713060401000,0,2914000,6824713063315000,2722,"R+",120,757,"ps",20463
+6824713060570000,1,23000,6824713060593000,15,"S",120,15,"ksoftirqd/1",17
+6824713060593000,1,54000,6824713060647000,5,"S",120,5,"rcu_preempt",7
+6824713060629000,2,431000,6824713061060000,777,"S",120,493,"HwBinder:640_1",721
 6824713060647000,1,274000,6824713060921000,0,"R",120,"[NULL]","swapper/5",0
-6824713060921000,1,103000,6824713061024000,40,"S",97,35,"DispSync",676
+6824713060921000,1,103000,6824713061024000,771,"S",97,493,"DispSync",676
 6824713061024000,1,915000,6824713061939000,0,"R",120,"[NULL]","swapper/5",0
 6824713061060000,2,1313000,6824713062373000,0,"R",120,"[NULL]","swapper/5",0
-6824713061939000,1,85000,6824713062024000,40,"S",97,35,"DispSync",676
+6824713061939000,1,85000,6824713062024000,771,"S",97,493,"DispSync",676
 6824713062024000,1,1073000,6824713063097000,0,"R",120,"[NULL]","swapper/5",0
-6824713062373000,2,326000,6824713062699000,41,"S",97,35,"app",678
+6824713062373000,2,326000,6824713062699000,773,"S",97,493,"app",678
 6824713062699000,2,2701000,6824713065400000,0,"R",120,"[NULL]","swapper/5",0
-6824713063058000,3,1709000,6824713064767000,42,"S",120,36,"ndroid.systemui",1664
-6824713063097000,1,28000,6824713063125000,40,"S",97,35,"DispSync",676
+6824713063058000,3,1709000,6824713064767000,644,"S",120,644,"ndroid.systemui",1664
+6824713063097000,1,28000,6824713063125000,771,"S",97,493,"DispSync",676
 6824713063125000,1,518000,6824713063643000,0,"R",120,"[NULL]","swapper/5",0
-6824713063315000,0,161000,6824713063476000,26,"S",49,23,"sugov:0",605
-6824713063476000,0,29511000,6824713092987000,84,"R+",120,68,"ps",20463
-6824713063643000,1,682000,6824713064325000,16,"S",120,14,"kworker/u16:7",19422
+6824713063315000,0,161000,6824713063476000,482,"S",49,482,"sugov:0",605
+6824713063476000,0,29511000,6824713092987000,2722,"R+",120,757,"ps",20463
+6824713063643000,1,682000,6824713064325000,702,"S",120,702,"kworker/u16:7",19422
 6824713064325000,1,588000,6824713064913000,0,"R",120,"[NULL]","swapper/5",0
 6824713064767000,3,15375000,6824713080142000,0,"R",120,"[NULL]","swapper/5",0
-6824713064913000,1,237000,6824713065150000,43,"S",120,35,"Binder:640_2",675
+6824713064913000,1,237000,6824713065150000,770,"S",120,493,"Binder:640_2",675
 6824713065150000,1,321000,6824713065471000,0,"R",120,"[NULL]","swapper/5",0
-6824713065400000,2,110000,6824713065510000,41,"S",97,35,"app",678
-6824713065471000,1,26000,6824713065497000,40,"S",97,35,"DispSync",676
+6824713065400000,2,110000,6824713065510000,773,"S",97,493,"app",678
+6824713065471000,1,26000,6824713065497000,771,"S",97,493,"DispSync",676
 6824713065497000,1,1148000,6824713066645000,0,"R",120,"[NULL]","swapper/5",0
 6824713065510000,2,1194000,6824713066704000,0,"R",120,"[NULL]","swapper/5",0
-6824713066645000,1,64000,6824713066709000,6,"S",120,4,"rcu_preempt",7
-6824713066704000,2,53000,6824713066757000,11,"S",120,9,"rcuop/0",10
+6824713066645000,1,64000,6824713066709000,5,"S",120,5,"rcu_preempt",7
+6824713066704000,2,53000,6824713066757000,8,"S",120,8,"rcuop/0",10
 6824713066709000,1,41000,6824713066750000,0,"R",120,"[NULL]","swapper/5",0
-6824713066750000,1,18000,6824713066768000,6,"S",120,4,"rcu_preempt",7
+6824713066750000,1,18000,6824713066768000,5,"S",120,5,"rcu_preempt",7
 6824713066757000,2,4703000,6824713071460000,0,"R",120,"[NULL]","swapper/5",0
 6824713066768000,1,4421000,6824713071189000,0,"R",120,"[NULL]","swapper/5",0
-6824713071189000,1,109000,6824713071298000,82,"S",120,5,"shell",20461
+6824713071189000,1,109000,6824713071298000,2728,"S",120,739,"shell",20461
 6824713071298000,1,679000,6824713071977000,0,"R",120,"[NULL]","swapper/5",0
-6824713071460000,2,412000,6824713071872000,7,"S",120,5,"adbd",20305
+6824713071460000,2,412000,6824713071872000,739,"S",120,739,"adbd",20305
 6824713071872000,2,322000,6824713072194000,0,"R",120,"[NULL]","swapper/5",0
-6824713071977000,1,50000,6824713072027000,2,"S",100,2,"kworker/u17:1",1134
-6824713072027000,1,34000,6824713072061000,48,"S",120,41,"kworker/1:1",18800
+6824713071977000,1,50000,6824713072027000,630,"S",100,630,"kworker/u17:1",1134
+6824713072027000,1,34000,6824713072061000,693,"S",120,693,"kworker/1:1",18800
 6824713072061000,1,129000,6824713072190000,0,"R",120,"[NULL]","swapper/5",0
-6824713072190000,1,20000,6824713072210000,2,"S",100,2,"kworker/u17:1",1134
-6824713072194000,2,45000,6824713072239000,4,"S",120,5,"UsbFfs-worker",20308
+6824713072190000,1,20000,6824713072210000,630,"S",100,630,"kworker/u17:1",1134
+6824713072194000,2,45000,6824713072239000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713072210000,1,337000,6824713072547000,0,"R",120,"[NULL]","swapper/5",0
 6824713072239000,2,259000,6824713072498000,0,"R",120,"[NULL]","swapper/5",0
-6824713072498000,2,31000,6824713072529000,2,"S",100,2,"kworker/u17:1",1134
-6824713072529000,2,26000,6824713072555000,31,"S",120,29,"kworker/2:0",18823
-6824713072547000,1,36000,6824713072583000,4,"S",120,5,"UsbFfs-worker",20308
+6824713072498000,2,31000,6824713072529000,630,"S",100,630,"kworker/u17:1",1134
+6824713072529000,2,26000,6824713072555000,694,"S",120,694,"kworker/2:0",18823
+6824713072547000,1,36000,6824713072583000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713072555000,2,85000,6824713072640000,0,"R",120,"[NULL]","swapper/5",0
 6824713072583000,1,374000,6824713072957000,0,"R",120,"[NULL]","swapper/5",0
-6824713072640000,2,21000,6824713072661000,2,"S",100,2,"kworker/u17:1",1134
+6824713072640000,2,21000,6824713072661000,630,"S",100,630,"kworker/u17:1",1134
 6824713072661000,2,249000,6824713072910000,0,"R",120,"[NULL]","swapper/5",0
-6824713072910000,2,25000,6824713072935000,2,"S",100,2,"kworker/u17:1",1134
-6824713072935000,2,32000,6824713072967000,31,"S",120,29,"kworker/2:0",18823
-6824713072957000,1,71000,6824713073028000,4,"S",120,5,"UsbFfs-worker",20308
+6824713072910000,2,25000,6824713072935000,630,"S",100,630,"kworker/u17:1",1134
+6824713072935000,2,32000,6824713072967000,694,"S",120,694,"kworker/2:0",18823
+6824713072957000,1,71000,6824713073028000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713072967000,2,23000,6824713072990000,0,"R",120,"[NULL]","swapper/5",0
-6824713072990000,2,99000,6824713073089000,7,"S",120,5,"adbd",20305
+6824713072990000,2,99000,6824713073089000,739,"S",120,739,"adbd",20305
 6824713073028000,1,235000,6824713073263000,0,"R",120,"[NULL]","swapper/5",0
 6824713073089000,2,196000,6824713073285000,0,"R",120,"[NULL]","swapper/5",0
-6824713073263000,1,25000,6824713073288000,6,"S",120,4,"rcu_preempt",7
-6824713073285000,2,117000,6824713073402000,11,"S",120,9,"rcuop/0",10
+6824713073263000,1,25000,6824713073288000,5,"S",120,5,"rcu_preempt",7
+6824713073285000,2,117000,6824713073402000,8,"S",120,8,"rcuop/0",10
 6824713073288000,1,270000,6824713073558000,0,"R",120,"[NULL]","swapper/5",0
 6824713073402000,2,3701000,6824713077103000,0,"R",120,"[NULL]","swapper/5",0
-6824713073558000,1,15000,6824713073573000,6,"S",120,4,"rcu_preempt",7
+6824713073558000,1,15000,6824713073573000,5,"S",120,5,"rcu_preempt",7
 6824713073573000,1,3170000,6824713076743000,0,"R",120,"[NULL]","swapper/5",0
-6824713076743000,1,148000,6824713076891000,16,"D",120,14,"kworker/u16:7",19422
+6824713076743000,1,148000,6824713076891000,702,"D",120,702,"kworker/u16:7",19422
 6824713076891000,1,199000,6824713077090000,0,"R",120,"[NULL]","swapper/5",0
-6824713077090000,1,16000,6824713077106000,16,"S",120,14,"kworker/u16:7",19422
-6824713077103000,2,19000,6824713077122000,32,"S",120,30,"smem_native_rpm",87
+6824713077090000,1,16000,6824713077106000,702,"S",120,702,"kworker/u16:7",19422
+6824713077103000,2,19000,6824713077122000,77,"S",120,77,"smem_native_rpm",87
 6824713077106000,1,2331000,6824713079437000,0,"R",120,"[NULL]","swapper/5",0
 6824713077122000,2,2542000,6824713079664000,0,"R",120,"[NULL]","swapper/5",0
-6824713079437000,1,81000,6824713079518000,82,"S",120,5,"shell",20461
+6824713079437000,1,81000,6824713079518000,2728,"S",120,739,"shell",20461
 6824713079518000,1,417000,6824713079935000,0,"R",120,"[NULL]","swapper/5",0
-6824713079664000,2,293000,6824713079957000,7,"S",120,5,"adbd",20305
-6824713079935000,1,31000,6824713079966000,2,"S",100,2,"kworker/u17:1",1134
+6824713079664000,2,293000,6824713079957000,739,"S",120,739,"adbd",20305
+6824713079935000,1,31000,6824713079966000,630,"S",100,630,"kworker/u17:1",1134
 6824713079957000,2,23000,6824713079980000,0,"R",120,"[NULL]","swapper/5",0
-6824713079966000,1,28000,6824713079994000,48,"S",120,41,"kworker/1:1",18800
-6824713079980000,2,20000,6824713080000000,2,"S",100,2,"kworker/u17:1",1134
-6824713079994000,1,24000,6824713080018000,4,"S",120,5,"UsbFfs-worker",20308
+6824713079966000,1,28000,6824713079994000,693,"S",120,693,"kworker/1:1",18800
+6824713079980000,2,20000,6824713080000000,630,"S",100,630,"kworker/u17:1",1134
+6824713079994000,1,24000,6824713080018000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713080000000,2,320000,6824713080320000,0,"R",120,"[NULL]","swapper/5",0
 6824713080018000,1,518000,6824713080536000,0,"R",120,"[NULL]","swapper/5",0
-6824713080142000,3,24000,6824713080166000,6,"S",120,4,"rcu_preempt",7
+6824713080142000,3,24000,6824713080166000,5,"S",120,5,"rcu_preempt",7
 6824713080166000,3,419000,6824713080585000,0,"R",120,"[NULL]","swapper/5",0
-6824713080320000,2,27000,6824713080347000,2,"S",100,2,"kworker/u17:1",1134
-6824713080347000,2,41000,6824713080388000,31,"S",120,29,"kworker/2:0",18823
+6824713080320000,2,27000,6824713080347000,630,"S",100,630,"kworker/u17:1",1134
+6824713080347000,2,41000,6824713080388000,694,"S",120,694,"kworker/2:0",18823
 6824713080388000,2,452000,6824713080840000,0,"R",120,"[NULL]","swapper/5",0
-6824713080536000,1,38000,6824713080574000,4,"S",120,5,"UsbFfs-worker",20308
+6824713080536000,1,38000,6824713080574000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713080574000,1,488000,6824713081062000,0,"R",120,"[NULL]","swapper/5",0
-6824713080585000,3,26000,6824713080611000,2,"S",100,2,"kworker/u17:1",1134
+6824713080585000,3,26000,6824713080611000,630,"S",100,630,"kworker/u17:1",1134
 6824713080611000,3,6419000,6824713087030000,0,"R",120,"[NULL]","swapper/5",0
-6824713080840000,2,23000,6824713080863000,2,"S",100,2,"kworker/u17:1",1134
-6824713080863000,2,43000,6824713080906000,31,"S",120,29,"kworker/2:0",18823
+6824713080840000,2,23000,6824713080863000,630,"S",100,630,"kworker/u17:1",1134
+6824713080863000,2,43000,6824713080906000,694,"S",120,694,"kworker/2:0",18823
 6824713080906000,2,345000,6824713081251000,0,"R",120,"[NULL]","swapper/5",0
-6824713081062000,1,72000,6824713081134000,4,"S",120,5,"UsbFfs-worker",20308
+6824713081062000,1,72000,6824713081134000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713081134000,1,6852000,6824713087986000,0,"R",120,"[NULL]","swapper/5",0
-6824713081251000,2,106000,6824713081357000,7,"S",120,5,"adbd",20305
+6824713081251000,2,106000,6824713081357000,739,"S",120,739,"adbd",20305
 6824713081357000,2,6049000,6824713087406000,0,"R",120,"[NULL]","swapper/5",0
-6824713087030000,3,62000,6824713087092000,6,"S",120,4,"rcu_preempt",7
+6824713087030000,3,62000,6824713087092000,5,"S",120,5,"rcu_preempt",7
 6824713087092000,3,964000,6824713088056000,0,"R",120,"[NULL]","swapper/5",0
-6824713087406000,2,284000,6824713087690000,11,"S",120,9,"rcuop/0",10
+6824713087406000,2,284000,6824713087690000,8,"S",120,8,"rcuop/0",10
 6824713087690000,2,783000,6824713088473000,0,"R",120,"[NULL]","swapper/5",0
-6824713087986000,1,139000,6824713088125000,82,"S",120,5,"shell",20461
-6824713088056000,3,22000,6824713088078000,6,"S",120,4,"rcu_preempt",7
+6824713087986000,1,139000,6824713088125000,2728,"S",120,739,"shell",20461
+6824713088056000,3,22000,6824713088078000,5,"S",120,5,"rcu_preempt",7
 6824713088078000,3,36000,6824713088114000,0,"R",120,"[NULL]","swapper/5",0
-6824713088114000,3,404000,6824713088518000,7,"S",120,5,"adbd",20305
+6824713088114000,3,404000,6824713088518000,739,"S",120,739,"adbd",20305
 6824713088125000,1,464000,6824713088589000,0,"R",120,"[NULL]","swapper/5",0
-6824713088473000,2,53000,6824713088526000,2,"S",100,2,"kworker/u17:1",1134
+6824713088473000,2,53000,6824713088526000,630,"S",100,630,"kworker/u17:1",1134
 6824713088518000,3,43000,6824713088561000,0,"R",120,"[NULL]","swapper/5",0
-6824713088526000,2,74000,6824713088600000,31,"S",120,29,"kworker/2:0",18823
-6824713088561000,3,34000,6824713088595000,2,"S",100,2,"kworker/u17:1",1134
-6824713088589000,1,37000,6824713088626000,4,"S",120,5,"UsbFfs-worker",20308
+6824713088526000,2,74000,6824713088600000,694,"S",120,694,"kworker/2:0",18823
+6824713088561000,3,34000,6824713088595000,630,"S",100,630,"kworker/u17:1",1134
+6824713088589000,1,37000,6824713088626000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713088595000,3,122000,6824713088717000,0,"R",120,"[NULL]","swapper/5",0
 6824713088600000,2,1021000,6824713089621000,0,"R",120,"[NULL]","swapper/5",0
 6824713088626000,1,590000,6824713089216000,0,"R",120,"[NULL]","swapper/5",0
-6824713088717000,3,48000,6824713088765000,2,"S",100,2,"kworker/u17:1",1134
-6824713088765000,3,41000,6824713088806000,49,"S",120,42,"kworker/3:1",17791
+6824713088717000,3,48000,6824713088765000,630,"S",100,630,"kworker/u17:1",1134
+6824713088765000,3,41000,6824713088806000,686,"S",120,686,"kworker/3:1",17791
 6824713088806000,3,14000,6824713088820000,0,"R",120,"[NULL]","swapper/5",0
-6824713088820000,3,17000,6824713088837000,2,"S",100,2,"kworker/u17:1",1134
+6824713088820000,3,17000,6824713088837000,630,"S",100,630,"kworker/u17:1",1134
 6824713088837000,3,135000,6824713088972000,0,"R",120,"[NULL]","swapper/5",0
-6824713088972000,3,44000,6824713089016000,2,"S",100,2,"kworker/u17:1",1134
+6824713088972000,3,44000,6824713089016000,630,"S",100,630,"kworker/u17:1",1134
 6824713089016000,3,47000,6824713089063000,0,"R",120,"[NULL]","swapper/5",0
-6824713089063000,3,35000,6824713089098000,2,"S",100,2,"kworker/u17:1",1134
-6824713089098000,3,89000,6824713089187000,49,"S",120,42,"kworker/3:1",17791
+6824713089063000,3,35000,6824713089098000,630,"S",100,630,"kworker/u17:1",1134
+6824713089098000,3,89000,6824713089187000,686,"S",120,686,"kworker/3:1",17791
 6824713089187000,3,4413000,6824713093600000,0,"R",120,"[NULL]","swapper/5",0
-6824713089216000,1,154000,6824713089370000,4,"S",120,5,"UsbFfs-worker",20308
+6824713089216000,1,154000,6824713089370000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713089370000,1,4023000,6824713093393000,0,"R",120,"[NULL]","swapper/5",0
-6824713089621000,2,127000,6824713089748000,7,"S",120,5,"adbd",20305
+6824713089621000,2,127000,6824713089748000,739,"S",120,739,"adbd",20305
 6824713089748000,2,4425000,6824713094173000,0,"R",120,"[NULL]","swapper/5",0
-6824713092987000,0,74000,6824713093061000,35,"S",120,33,"kworker/0:5",20371
-6824713093061000,0,3580000,6824713096641000,84,"R+",120,68,"ps",20463
-6824713093393000,1,580000,6824713093973000,36,"S",111,34,"SDM_EventThread",685
-6824713093600000,3,36000,6824713093636000,6,"S",120,4,"rcu_preempt",7
+6824713092987000,0,74000,6824713093061000,743,"S",120,743,"kworker/0:5",20371
+6824713093061000,0,3580000,6824713096641000,2722,"R+",120,757,"ps",20463
+6824713093393000,1,580000,6824713093973000,786,"S",111,494,"SDM_EventThread",685
+6824713093600000,3,36000,6824713093636000,5,"S",120,5,"rcu_preempt",7
 6824713093636000,3,1371000,6824713095007000,0,"R",120,"[NULL]","swapper/5",0
 6824713093973000,1,507000,6824713094480000,0,"R",120,"[NULL]","swapper/5",0
-6824713094173000,2,367000,6824713094540000,38,"S",120,35,"HwBinder:640_1",721
-6824713094480000,1,99000,6824713094579000,40,"S",97,35,"DispSync",676
+6824713094173000,2,367000,6824713094540000,777,"S",120,493,"HwBinder:640_1",721
+6824713094480000,1,99000,6824713094579000,771,"S",97,493,"DispSync",676
 6824713094540000,2,9000,6824713094549000,0,"R",120,"[NULL]","swapper/5",0
-6824713094549000,2,126000,6824713094675000,38,"S",120,35,"HwBinder:640_1",721
+6824713094549000,2,126000,6824713094675000,777,"S",120,493,"HwBinder:640_1",721
 6824713094579000,1,9000,6824713094588000,0,"R",120,"[NULL]","swapper/5",0
-6824713094588000,1,74000,6824713094662000,40,"S",97,35,"DispSync",676
+6824713094588000,1,74000,6824713094662000,771,"S",97,493,"DispSync",676
 6824713094662000,1,934000,6824713095596000,0,"R",120,"[NULL]","swapper/5",0
 6824713094675000,2,1004000,6824713095679000,0,"R",120,"[NULL]","swapper/5",0
-6824713095007000,3,267000,6824713095274000,41,"S",97,35,"app",678
+6824713095007000,3,267000,6824713095274000,773,"S",97,493,"app",678
 6824713095274000,3,5029000,6824713100303000,0,"R",120,"[NULL]","swapper/5",0
-6824713095596000,1,2136000,6824713097732000,42,"S",120,36,"ndroid.systemui",1664
-6824713095679000,2,17000,6824713095696000,40,"S",97,35,"DispSync",676
+6824713095596000,1,2136000,6824713097732000,644,"S",120,644,"ndroid.systemui",1664
+6824713095679000,2,17000,6824713095696000,771,"S",97,493,"DispSync",676
 6824713095696000,2,2152000,6824713097848000,0,"R",120,"[NULL]","swapper/5",0
-6824713096641000,0,233000,6824713096874000,26,"S",49,23,"sugov:0",605
-6824713096874000,0,29589000,6824713126463000,84,"R+",120,68,"ps",20463
+6824713096641000,0,233000,6824713096874000,482,"S",49,482,"sugov:0",605
+6824713096874000,0,29589000,6824713126463000,2722,"R+",120,757,"ps",20463
 6824713097732000,1,701000,6824713098433000,0,"R",120,"[NULL]","swapper/5",0
-6824713097848000,2,321000,6824713098169000,43,"S",120,35,"Binder:640_2",675
+6824713097848000,2,321000,6824713098169000,770,"S",120,493,"Binder:640_2",675
 6824713098169000,2,724000,6824713098893000,0,"R",120,"[NULL]","swapper/5",0
-6824713098433000,1,171000,6824713098604000,41,"S",97,35,"app",678
+6824713098433000,1,171000,6824713098604000,773,"S",97,493,"app",678
 6824713098604000,1,39000,6824713098643000,0,"R",120,"[NULL]","swapper/5",0
-6824713098643000,1,113000,6824713098756000,26,"S",49,23,"sugov:0",605
+6824713098643000,1,113000,6824713098756000,482,"S",49,482,"sugov:0",605
 6824713098756000,1,488000,6824713099244000,0,"R",120,"[NULL]","swapper/5",0
-6824713098893000,2,25000,6824713098918000,40,"S",97,35,"DispSync",676
+6824713098893000,2,25000,6824713098918000,771,"S",97,493,"DispSync",676
 6824713098918000,2,480000,6824713099398000,0,"R",120,"[NULL]","swapper/5",0
-6824713099244000,1,183000,6824713099427000,82,"S",120,5,"shell",20461
-6824713099398000,2,1576000,6824713100974000,7,"S",120,5,"adbd",20305
+6824713099244000,1,183000,6824713099427000,2728,"S",120,739,"shell",20461
+6824713099398000,2,1576000,6824713100974000,739,"S",120,739,"adbd",20305
 6824713099427000,1,51000,6824713099478000,0,"R",120,"[NULL]","swapper/5",0
-6824713099478000,1,65000,6824713099543000,50,"S",120,43,"Executor-7",14762
+6824713099478000,1,65000,6824713099543000,1919,"S",120,667,"Executor-7",14762
 6824713099543000,1,1075000,6824713100618000,0,"R",120,"[NULL]","swapper/5",0
-6824713100303000,3,67000,6824713100370000,2,"S",100,2,"kworker/u17:1",1134
+6824713100303000,3,67000,6824713100370000,630,"S",100,630,"kworker/u17:1",1134
 6824713100370000,3,9000,6824713100379000,0,"R",120,"[NULL]","swapper/5",0
-6824713100379000,3,72000,6824713100451000,2,"S",100,2,"kworker/u17:1",1134
-6824713100451000,3,97000,6824713100548000,49,"S",120,42,"kworker/3:1",17791
-6824713100548000,3,59000,6824713100607000,4,"S",120,5,"UsbFfs-worker",20308
-6824713100607000,3,31000,6824713100638000,2,"S",100,2,"kworker/u17:1",1134
-6824713100618000,1,54000,6824713100672000,6,"S",120,4,"rcu_preempt",7
-6824713100638000,3,17000,6824713100655000,49,"S",120,42,"kworker/3:1",17791
-6824713100655000,3,15000,6824713100670000,4,"R",120,5,"UsbFfs-worker",20308
-6824713100670000,3,12000,6824713100682000,2,"S",100,2,"kworker/u17:1",1134
-6824713100672000,1,561000,6824713101233000,11,"S",120,9,"rcuop/0",10
-6824713100682000,3,60000,6824713100742000,4,"S",120,5,"UsbFfs-worker",20308
+6824713100379000,3,72000,6824713100451000,630,"S",100,630,"kworker/u17:1",1134
+6824713100451000,3,97000,6824713100548000,686,"S",120,686,"kworker/3:1",17791
+6824713100548000,3,59000,6824713100607000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713100607000,3,31000,6824713100638000,630,"S",100,630,"kworker/u17:1",1134
+6824713100618000,1,54000,6824713100672000,5,"S",120,5,"rcu_preempt",7
+6824713100638000,3,17000,6824713100655000,686,"S",120,686,"kworker/3:1",17791
+6824713100655000,3,15000,6824713100670000,2487,"R",120,739,"UsbFfs-worker",20308
+6824713100670000,3,12000,6824713100682000,630,"S",100,630,"kworker/u17:1",1134
+6824713100672000,1,561000,6824713101233000,8,"S",120,8,"rcuop/0",10
+6824713100682000,3,60000,6824713100742000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713100742000,3,513000,6824713101255000,0,"R",120,"[NULL]","swapper/5",0
 6824713100974000,2,632000,6824713101606000,0,"R",120,"[NULL]","swapper/5",0
 6824713101233000,1,787000,6824713102020000,0,"R",120,"[NULL]","swapper/5",0
-6824713101255000,3,47000,6824713101302000,2,"S",100,2,"kworker/u17:1",1134
+6824713101255000,3,47000,6824713101302000,630,"S",100,630,"kworker/u17:1",1134
 6824713101302000,3,605000,6824713101907000,0,"R",120,"[NULL]","swapper/5",0
-6824713101606000,2,31000,6824713101637000,6,"S",120,4,"rcu_preempt",7
+6824713101606000,2,31000,6824713101637000,5,"S",120,5,"rcu_preempt",7
 6824713101637000,2,471000,6824713102108000,0,"R",120,"[NULL]","swapper/5",0
-6824713101907000,3,54000,6824713101961000,2,"S",100,2,"kworker/u17:1",1134
-6824713101961000,3,73000,6824713102034000,49,"S",120,42,"kworker/3:1",17791
-6824713102020000,1,177000,6824713102197000,4,"S",120,5,"UsbFfs-worker",20308
+6824713101907000,3,54000,6824713101961000,630,"S",100,630,"kworker/u17:1",1134
+6824713101961000,3,73000,6824713102034000,686,"S",120,686,"kworker/3:1",17791
+6824713102020000,1,177000,6824713102197000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713102034000,3,8819000,6824713110853000,0,"R",120,"[NULL]","swapper/5",0
-6824713102108000,2,177000,6824713102285000,7,"S",120,5,"adbd",20305
+6824713102108000,2,177000,6824713102285000,739,"S",120,739,"adbd",20305
 6824713102197000,1,5230000,6824713107427000,0,"R",120,"[NULL]","swapper/5",0
 6824713102285000,2,4714000,6824713106999000,0,"R",120,"[NULL]","swapper/5",0
-6824713106999000,2,46000,6824713107045000,6,"S",120,4,"rcu_preempt",7
+6824713106999000,2,46000,6824713107045000,5,"S",120,5,"rcu_preempt",7
 6824713107045000,2,1111000,6824713108156000,0,"R",120,"[NULL]","swapper/5",0
-6824713107427000,1,382000,6824713107809000,11,"S",120,9,"rcuop/0",10
+6824713107427000,1,382000,6824713107809000,8,"S",120,8,"rcuop/0",10
 6824713107809000,1,1784000,6824713109593000,0,"R",120,"[NULL]","swapper/5",0
-6824713108156000,2,24000,6824713108180000,6,"S",120,4,"rcu_preempt",7
+6824713108156000,2,24000,6824713108180000,5,"S",120,5,"rcu_preempt",7
 6824713108180000,2,1871000,6824713110051000,0,"R",120,"[NULL]","swapper/5",0
-6824713109593000,1,155000,6824713109748000,82,"S",120,5,"shell",20461
+6824713109593000,1,155000,6824713109748000,2728,"S",120,739,"shell",20461
 6824713109748000,1,1604000,6824713111352000,0,"R",120,"[NULL]","swapper/5",0
-6824713110051000,2,506000,6824713110557000,7,"S",120,5,"adbd",20305
+6824713110051000,2,506000,6824713110557000,739,"S",120,739,"adbd",20305
 6824713110557000,2,728000,6824713111285000,0,"R",120,"[NULL]","swapper/5",0
-6824713110853000,3,66000,6824713110919000,2,"S",100,2,"kworker/u17:1",1134
-6824713110919000,3,54000,6824713110973000,49,"S",120,42,"kworker/3:1",17791
+6824713110853000,3,66000,6824713110919000,630,"S",100,630,"kworker/u17:1",1134
+6824713110919000,3,54000,6824713110973000,686,"S",120,686,"kworker/3:1",17791
 6824713110973000,3,1252000,6824713112225000,0,"R",120,"[NULL]","swapper/5",0
-6824713111285000,2,35000,6824713111320000,2,"S",100,2,"kworker/u17:1",1134
+6824713111285000,2,35000,6824713111320000,630,"S",100,630,"kworker/u17:1",1134
 6824713111320000,2,474000,6824713111794000,0,"R",120,"[NULL]","swapper/5",0
-6824713111352000,1,75000,6824713111427000,4,"S",120,5,"UsbFfs-worker",20308
+6824713111352000,1,75000,6824713111427000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713111427000,1,882000,6824713112309000,0,"R",120,"[NULL]","swapper/5",0
-6824713111794000,2,71000,6824713111865000,2,"S",100,2,"kworker/u17:1",1134
-6824713111865000,2,64000,6824713111929000,31,"S",120,29,"kworker/2:0",18823
+6824713111794000,2,71000,6824713111865000,630,"S",100,630,"kworker/u17:1",1134
+6824713111865000,2,64000,6824713111929000,694,"S",120,694,"kworker/2:0",18823
 6824713111929000,2,466000,6824713112395000,0,"R",120,"[NULL]","swapper/5",0
-6824713112225000,3,47000,6824713112272000,2,"S",100,2,"kworker/u17:1",1134
-6824713112272000,3,72000,6824713112344000,49,"S",120,42,"kworker/3:1",17791
-6824713112309000,1,168000,6824713112477000,4,"S",120,5,"UsbFfs-worker",20308
+6824713112225000,3,47000,6824713112272000,630,"S",100,630,"kworker/u17:1",1134
+6824713112272000,3,72000,6824713112344000,686,"S",120,686,"kworker/3:1",17791
+6824713112309000,1,168000,6824713112477000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713112344000,3,10051000,6824713122395000,0,"R",120,"[NULL]","swapper/5",0
-6824713112395000,2,142000,6824713112537000,7,"S",120,5,"adbd",20305
+6824713112395000,2,142000,6824713112537000,739,"S",120,739,"adbd",20305
 6824713112477000,1,4894000,6824713117371000,0,"R",120,"[NULL]","swapper/5",0
 6824713112537000,2,766000,6824713113303000,0,"R",120,"[NULL]","swapper/5",0
-6824713113303000,2,30000,6824713113333000,6,"S",120,4,"rcu_preempt",7
+6824713113303000,2,30000,6824713113333000,5,"S",120,5,"rcu_preempt",7
 6824713113333000,2,3671000,6824713117004000,0,"R",120,"[NULL]","swapper/5",0
-6824713117004000,2,42000,6824713117046000,6,"S",120,4,"rcu_preempt",7
+6824713117004000,2,42000,6824713117046000,5,"S",120,5,"rcu_preempt",7
 6824713117046000,2,851000,6824713117897000,0,"R",120,"[NULL]","swapper/5",0
-6824713117371000,1,213000,6824713117584000,11,"S",120,9,"rcuop/0",10
+6824713117371000,1,213000,6824713117584000,8,"S",120,8,"rcuop/0",10
 6824713117584000,1,3602000,6824713121186000,0,"R",120,"[NULL]","swapper/5",0
-6824713117897000,2,22000,6824713117919000,6,"S",120,4,"rcu_preempt",7
+6824713117897000,2,22000,6824713117919000,5,"S",120,5,"rcu_preempt",7
 6824713117919000,2,3713000,6824713121632000,0,"R",120,"[NULL]","swapper/5",0
-6824713121186000,1,154000,6824713121340000,82,"S",120,5,"shell",20461
+6824713121186000,1,154000,6824713121340000,2728,"S",120,739,"shell",20461
 6824713121340000,1,1525000,6824713122865000,0,"R",120,"[NULL]","swapper/5",0
-6824713121632000,2,519000,6824713122151000,7,"S",120,5,"adbd",20305
+6824713121632000,2,519000,6824713122151000,739,"S",120,739,"adbd",20305
 6824713122151000,2,341000,6824713122492000,0,"R",120,"[NULL]","swapper/5",0
-6824713122395000,3,74000,6824713122469000,2,"S",100,2,"kworker/u17:1",1134
-6824713122469000,3,51000,6824713122520000,49,"S",120,42,"kworker/3:1",17791
-6824713122492000,2,31000,6824713122523000,2,"S",100,2,"kworker/u17:1",1134
+6824713122395000,3,74000,6824713122469000,630,"S",100,630,"kworker/u17:1",1134
+6824713122469000,3,51000,6824713122520000,686,"S",120,686,"kworker/3:1",17791
+6824713122492000,2,31000,6824713122523000,630,"S",100,630,"kworker/u17:1",1134
 6824713122520000,3,1113000,6824713123633000,0,"R",120,"[NULL]","swapper/5",0
 6824713122523000,2,120000,6824713122643000,0,"R",120,"[NULL]","swapper/5",0
-6824713122643000,2,45000,6824713122688000,2,"S",100,2,"kworker/u17:1",1134
-6824713122688000,2,30000,6824713122718000,31,"S",120,29,"kworker/2:0",18823
+6824713122643000,2,45000,6824713122688000,630,"S",100,630,"kworker/u17:1",1134
+6824713122688000,2,30000,6824713122718000,694,"S",120,694,"kworker/2:0",18823
 6824713122718000,2,23000,6824713122741000,0,"R",120,"[NULL]","swapper/5",0
-6824713122741000,2,16000,6824713122757000,2,"S",100,2,"kworker/u17:1",1134
+6824713122741000,2,16000,6824713122757000,630,"S",100,630,"kworker/u17:1",1134
 6824713122757000,2,136000,6824713122893000,0,"R",120,"[NULL]","swapper/5",0
-6824713122865000,1,95000,6824713122960000,4,"S",120,5,"UsbFfs-worker",20308
-6824713122893000,2,34000,6824713122927000,2,"S",100,2,"kworker/u17:1",1134
+6824713122865000,1,95000,6824713122960000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713122893000,2,34000,6824713122927000,630,"S",100,630,"kworker/u17:1",1134
 6824713122927000,2,101000,6824713123028000,0,"R",120,"[NULL]","swapper/5",0
 6824713122960000,1,168000,6824713123128000,0,"R",120,"[NULL]","swapper/5",0
-6824713123028000,2,42000,6824713123070000,2,"S",100,2,"kworker/u17:1",1134
-6824713123070000,2,76000,6824713123146000,31,"S",120,29,"kworker/2:0",18823
-6824713123128000,1,152000,6824713123280000,4,"S",120,5,"UsbFfs-worker",20308
+6824713123028000,2,42000,6824713123070000,630,"S",100,630,"kworker/u17:1",1134
+6824713123070000,2,76000,6824713123146000,694,"S",120,694,"kworker/2:0",18823
+6824713123128000,1,152000,6824713123280000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713123146000,2,29000,6824713123175000,0,"R",120,"[NULL]","swapper/5",0
-6824713123175000,2,190000,6824713123365000,7,"S",120,5,"adbd",20305
+6824713123175000,2,190000,6824713123365000,739,"S",120,739,"adbd",20305
 6824713123280000,1,3559000,6824713126839000,0,"R",120,"[NULL]","swapper/5",0
 6824713123365000,2,3548000,6824713126913000,0,"R",120,"[NULL]","swapper/5",0
-6824713123633000,3,30000,6824713123663000,6,"S",120,4,"rcu_preempt",7
+6824713123633000,3,30000,6824713123663000,5,"S",120,5,"rcu_preempt",7
 6824713123663000,3,3867000,6824713127530000,0,"R",120,"[NULL]","swapper/5",0
-6824713126463000,0,72000,6824713126535000,35,"S",120,33,"kworker/0:5",20371
-6824713126535000,0,3430000,6824713129965000,84,"R+",120,68,"ps",20463
-6824713126839000,1,582000,6824713127421000,36,"S",111,34,"SDM_EventThread",685
-6824713126913000,2,467000,6824713127380000,16,"R+",120,14,"kworker/u16:7",19422
-6824713127380000,2,198000,6824713127578000,40,"S",97,35,"DispSync",676
+6824713126463000,0,72000,6824713126535000,743,"S",120,743,"kworker/0:5",20371
+6824713126535000,0,3430000,6824713129965000,2722,"R+",120,757,"ps",20463
+6824713126839000,1,582000,6824713127421000,786,"S",111,494,"SDM_EventThread",685
+6824713126913000,2,467000,6824713127380000,702,"R+",120,702,"kworker/u16:7",19422
+6824713127380000,2,198000,6824713127578000,771,"S",97,493,"DispSync",676
 6824713127421000,1,507000,6824713127928000,0,"R",120,"[NULL]","swapper/5",0
-6824713127530000,3,44000,6824713127574000,32,"S",120,30,"smem_native_rpm",87
+6824713127530000,3,44000,6824713127574000,77,"S",120,77,"smem_native_rpm",87
 6824713127574000,3,559000,6824713128133000,0,"R",120,"[NULL]","swapper/5",0
-6824713127578000,2,33000,6824713127611000,16,"S",120,14,"kworker/u16:7",19422
-6824713127611000,2,338000,6824713127949000,38,"S",120,35,"HwBinder:640_1",721
-6824713127928000,1,258000,6824713128186000,41,"S",97,35,"app",678
+6824713127578000,2,33000,6824713127611000,702,"S",120,702,"kworker/u16:7",19422
+6824713127611000,2,338000,6824713127949000,777,"S",120,493,"HwBinder:640_1",721
+6824713127928000,1,258000,6824713128186000,773,"S",97,493,"app",678
 6824713127949000,2,518000,6824713128467000,0,"R",120,"[NULL]","swapper/5",0
-6824713128133000,3,26000,6824713128159000,40,"S",97,35,"DispSync",676
+6824713128133000,3,26000,6824713128159000,771,"S",97,493,"DispSync",676
 6824713128159000,3,8000,6824713128167000,0,"R",120,"[NULL]","swapper/5",0
-6824713128167000,3,13000,6824713128180000,40,"S",97,35,"DispSync",676
+6824713128167000,3,13000,6824713128180000,771,"S",97,493,"DispSync",676
 6824713128180000,3,1804000,6824713129984000,0,"R",120,"[NULL]","swapper/5",0
 6824713128186000,1,2334000,6824713130520000,0,"R",120,"[NULL]","swapper/5",0
-6824713128467000,2,1954000,6824713130421000,42,"S",120,36,"ndroid.systemui",1664
-6824713129965000,0,223000,6824713130188000,26,"S",49,23,"sugov:0",605
-6824713129984000,3,64000,6824713130048000,6,"S",120,4,"rcu_preempt",7
+6824713128467000,2,1954000,6824713130421000,644,"S",120,644,"ndroid.systemui",1664
+6824713129965000,0,223000,6824713130188000,482,"S",49,482,"sugov:0",605
+6824713129984000,3,64000,6824713130048000,5,"S",120,5,"rcu_preempt",7
 6824713130048000,3,1593000,6824713131641000,0,"R",120,"[NULL]","swapper/5",0
-6824713130188000,0,29833000,6824713160021000,84,"R",120,68,"ps",20463
+6824713130188000,0,29833000,6824713160021000,2722,"R",120,757,"ps",20463
 6824713130421000,2,758000,6824713131179000,0,"R",120,"[NULL]","swapper/5",0
-6824713130520000,1,356000,6824713130876000,43,"S",120,35,"Binder:640_2",675
-6824713130876000,1,61000,6824713130937000,11,"R",120,9,"rcuop/0",10
-6824713130937000,1,114000,6824713131051000,26,"S",49,23,"sugov:0",605
-6824713131051000,1,355000,6824713131406000,11,"S",120,9,"rcuop/0",10
-6824713131179000,2,114000,6824713131293000,41,"S",97,35,"app",678
+6824713130520000,1,356000,6824713130876000,770,"S",120,493,"Binder:640_2",675
+6824713130876000,1,61000,6824713130937000,8,"R",120,8,"rcuop/0",10
+6824713130937000,1,114000,6824713131051000,482,"S",49,482,"sugov:0",605
+6824713131051000,1,355000,6824713131406000,8,"S",120,8,"rcuop/0",10
+6824713131179000,2,114000,6824713131293000,773,"S",97,493,"app",678
 6824713131293000,2,593000,6824713131886000,0,"R",120,"[NULL]","swapper/5",0
 6824713131406000,1,828000,6824713132234000,0,"R",120,"[NULL]","swapper/5",0
-6824713131641000,3,30000,6824713131671000,40,"S",97,35,"DispSync",676
+6824713131641000,3,30000,6824713131671000,771,"S",97,493,"DispSync",676
 6824713131671000,3,95000,6824713131766000,0,"R",120,"[NULL]","swapper/5",0
-6824713131766000,3,183000,6824713131949000,82,"S",120,5,"shell",20461
-6824713131886000,2,26000,6824713131912000,6,"S",120,4,"rcu_preempt",7
+6824713131766000,3,183000,6824713131949000,2728,"S",120,739,"shell",20461
+6824713131886000,2,26000,6824713131912000,5,"S",120,5,"rcu_preempt",7
 6824713131912000,2,1066000,6824713132978000,0,"R",120,"[NULL]","swapper/5",0
 6824713131949000,3,1543000,6824713133492000,0,"R",120,"[NULL]","swapper/5",0
-6824713132234000,1,838000,6824713133072000,7,"S",120,5,"adbd",20305
-6824713132978000,2,164000,6824713133142000,2,"S",100,2,"kworker/u17:1",1134
+6824713132234000,1,838000,6824713133072000,739,"S",120,739,"adbd",20305
+6824713132978000,2,164000,6824713133142000,630,"S",100,630,"kworker/u17:1",1134
 6824713133072000,1,496000,6824713133568000,0,"R",120,"[NULL]","swapper/5",0
-6824713133142000,2,65000,6824713133207000,31,"S",120,29,"kworker/2:0",18823
+6824713133142000,2,65000,6824713133207000,694,"S",120,694,"kworker/2:0",18823
 6824713133207000,2,70000,6824713133277000,0,"R",120,"[NULL]","swapper/5",0
-6824713133277000,2,50000,6824713133327000,2,"S",100,2,"kworker/u17:1",1134
-6824713133327000,2,20000,6824713133347000,31,"S",120,29,"kworker/2:0",18823
+6824713133277000,2,50000,6824713133327000,630,"S",100,630,"kworker/u17:1",1134
+6824713133327000,2,20000,6824713133347000,694,"S",120,694,"kworker/2:0",18823
 6824713133347000,2,160000,6824713133507000,0,"R",120,"[NULL]","swapper/5",0
-6824713133492000,3,53000,6824713133545000,79,"S",100,64,"kworker/u17:2",14944
-6824713133507000,2,8000,6824713133515000,2,"S",100,2,"kworker/u17:1",1134
+6824713133492000,3,53000,6824713133545000,670,"S",100,670,"kworker/u17:2",14944
+6824713133507000,2,8000,6824713133515000,630,"S",100,630,"kworker/u17:1",1134
 6824713133515000,2,65000,6824713133580000,0,"R",120,"[NULL]","swapper/5",0
 6824713133545000,3,9733000,6824713143278000,0,"R",120,"[NULL]","swapper/5",0
-6824713133568000,1,104000,6824713133672000,4,"S",120,5,"UsbFfs-worker",20308
-6824713133580000,2,40000,6824713133620000,79,"S",100,64,"kworker/u17:2",14944
-6824713133620000,2,86000,6824713133706000,31,"S",120,29,"kworker/2:0",18823
+6824713133568000,1,104000,6824713133672000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713133580000,2,40000,6824713133620000,670,"S",100,670,"kworker/u17:2",14944
+6824713133620000,2,86000,6824713133706000,694,"S",120,694,"kworker/2:0",18823
 6824713133672000,1,11000,6824713133683000,0,"R",120,"[NULL]","swapper/5",0
-6824713133683000,1,142000,6824713133825000,4,"S",120,5,"UsbFfs-worker",20308
+6824713133683000,1,142000,6824713133825000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713133706000,2,37000,6824713133743000,0,"R",120,"[NULL]","swapper/5",0
-6824713133743000,2,140000,6824713133883000,7,"S",120,5,"adbd",20305
+6824713133743000,2,140000,6824713133883000,739,"S",120,739,"adbd",20305
 6824713133825000,1,6827000,6824713140652000,0,"R",120,"[NULL]","swapper/5",0
 6824713133883000,2,3063000,6824713136946000,0,"R",120,"[NULL]","swapper/5",0
-6824713136946000,2,41000,6824713136987000,6,"S",120,4,"rcu_preempt",7
+6824713136946000,2,41000,6824713136987000,5,"S",120,5,"rcu_preempt",7
 6824713136987000,2,3274000,6824713140261000,0,"R",120,"[NULL]","swapper/5",0
-6824713140261000,2,37000,6824713140298000,6,"S",120,4,"rcu_preempt",7
+6824713140261000,2,37000,6824713140298000,5,"S",120,5,"rcu_preempt",7
 6824713140298000,2,1112000,6824713141410000,0,"R",120,"[NULL]","swapper/5",0
-6824713140652000,1,423000,6824713141075000,11,"S",120,9,"rcuop/0",10
+6824713140652000,1,423000,6824713141075000,8,"S",120,8,"rcuop/0",10
 6824713141075000,1,1022000,6824713142097000,0,"R",120,"[NULL]","swapper/5",0
-6824713141410000,2,18000,6824713141428000,6,"S",120,4,"rcu_preempt",7
+6824713141410000,2,18000,6824713141428000,5,"S",120,5,"rcu_preempt",7
 6824713141428000,2,1111000,6824713142539000,0,"R",120,"[NULL]","swapper/5",0
-6824713142097000,1,152000,6824713142249000,82,"S",120,5,"shell",20461
+6824713142097000,1,152000,6824713142249000,2728,"S",120,739,"shell",20461
 6824713142249000,1,2185000,6824713144434000,0,"R",120,"[NULL]","swapper/5",0
-6824713142539000,2,518000,6824713143057000,7,"S",120,5,"adbd",20305
+6824713142539000,2,518000,6824713143057000,739,"S",120,739,"adbd",20305
 6824713143057000,2,1528000,6824713144585000,0,"R",120,"[NULL]","swapper/5",0
-6824713143278000,3,57000,6824713143335000,79,"S",100,64,"kworker/u17:2",14944
+6824713143278000,3,57000,6824713143335000,670,"S",100,670,"kworker/u17:2",14944
 6824713143335000,3,606000,6824713143941000,0,"R",120,"[NULL]","swapper/5",0
-6824713143941000,3,76000,6824713144017000,79,"S",100,64,"kworker/u17:2",14944
-6824713144017000,3,60000,6824713144077000,49,"S",120,42,"kworker/3:1",17791
+6824713143941000,3,76000,6824713144017000,670,"S",100,670,"kworker/u17:2",14944
+6824713144017000,3,60000,6824713144077000,686,"S",120,686,"kworker/3:1",17791
 6824713144077000,3,438000,6824713144515000,0,"R",120,"[NULL]","swapper/5",0
-6824713144434000,1,56000,6824713144490000,4,"S",120,5,"UsbFfs-worker",20308
+6824713144434000,1,56000,6824713144490000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713144490000,1,687000,6824713145177000,0,"R",120,"[NULL]","swapper/5",0
-6824713144515000,3,36000,6824713144551000,79,"S",100,64,"kworker/u17:2",14944
-6824713144551000,3,44000,6824713144595000,49,"S",120,42,"kworker/3:1",17791
-6824713144585000,2,61000,6824713144646000,4,"S",120,5,"UsbFfs-worker",20308
-6824713144595000,3,37000,6824713144632000,79,"S",100,64,"kworker/u17:2",14944
+6824713144515000,3,36000,6824713144551000,670,"S",100,670,"kworker/u17:2",14944
+6824713144551000,3,44000,6824713144595000,686,"S",120,686,"kworker/3:1",17791
+6824713144585000,2,61000,6824713144646000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713144595000,3,37000,6824713144632000,670,"S",100,670,"kworker/u17:2",14944
 6824713144632000,3,139000,6824713144771000,0,"R",120,"[NULL]","swapper/5",0
 6824713144646000,2,26000,6824713144672000,0,"R",120,"[NULL]","swapper/5",0
-6824713144672000,2,58000,6824713144730000,79,"S",100,64,"kworker/u17:2",14944
-6824713144730000,2,57000,6824713144787000,31,"S",120,29,"kworker/2:0",18823
-6824713144771000,3,137000,6824713144908000,4,"S",120,5,"UsbFfs-worker",20308
+6824713144672000,2,58000,6824713144730000,670,"S",100,670,"kworker/u17:2",14944
+6824713144730000,2,57000,6824713144787000,694,"S",120,694,"kworker/2:0",18823
+6824713144771000,3,137000,6824713144908000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713144787000,2,2129000,6824713146916000,0,"R",120,"[NULL]","swapper/5",0
 6824713144908000,3,9292000,6824713154200000,0,"R",120,"[NULL]","swapper/5",0
-6824713145177000,1,151000,6824713145328000,7,"S",120,5,"adbd",20305
+6824713145177000,1,151000,6824713145328000,739,"S",120,739,"adbd",20305
 6824713145328000,1,1971000,6824713147299000,0,"R",120,"[NULL]","swapper/5",0
-6824713146916000,2,42000,6824713146958000,6,"S",120,4,"rcu_preempt",7
+6824713146916000,2,42000,6824713146958000,5,"S",120,5,"rcu_preempt",7
 6824713146958000,2,950000,6824713147908000,0,"R",120,"[NULL]","swapper/5",0
-6824713147299000,1,265000,6824713147564000,11,"S",120,9,"rcuop/0",10
+6824713147299000,1,265000,6824713147564000,8,"S",120,8,"rcuop/0",10
 6824713147564000,1,6404000,6824713153968000,0,"R",120,"[NULL]","swapper/5",0
-6824713147908000,2,20000,6824713147928000,6,"S",120,4,"rcu_preempt",7
+6824713147908000,2,20000,6824713147928000,5,"S",120,5,"rcu_preempt",7
 6824713147928000,2,5718000,6824713153646000,0,"R",120,"[NULL]","swapper/5",0
-6824713153646000,2,49000,6824713153695000,6,"S",120,4,"rcu_preempt",7
+6824713153646000,2,49000,6824713153695000,5,"S",120,5,"rcu_preempt",7
 6824713153695000,2,724000,6824713154419000,0,"R",120,"[NULL]","swapper/5",0
-6824713153968000,1,154000,6824713154122000,82,"S",120,5,"shell",20461
+6824713153968000,1,154000,6824713154122000,2728,"S",120,739,"shell",20461
 6824713154122000,1,591000,6824713154713000,0,"R",120,"[NULL]","swapper/5",0
-6824713154200000,3,176000,6824713154376000,11,"S",120,9,"rcuop/0",10
+6824713154200000,3,176000,6824713154376000,8,"S",120,8,"rcuop/0",10
 6824713154376000,3,873000,6824713155249000,0,"R",120,"[NULL]","swapper/5",0
-6824713154419000,2,564000,6824713154983000,7,"S",120,5,"adbd",20305
-6824713154713000,1,18000,6824713154731000,6,"S",120,4,"rcu_preempt",7
+6824713154419000,2,564000,6824713154983000,739,"S",120,739,"adbd",20305
+6824713154713000,1,18000,6824713154731000,5,"S",120,5,"rcu_preempt",7
 6824713154731000,1,1001000,6824713155732000,0,"R",120,"[NULL]","swapper/5",0
 6824713154983000,2,683000,6824713155666000,0,"R",120,"[NULL]","swapper/5",0
-6824713155249000,3,83000,6824713155332000,79,"S",100,64,"kworker/u17:2",14944
-6824713155332000,3,55000,6824713155387000,49,"S",120,42,"kworker/3:1",17791
+6824713155249000,3,83000,6824713155332000,670,"S",100,670,"kworker/u17:2",14944
+6824713155332000,3,55000,6824713155387000,686,"S",120,686,"kworker/3:1",17791
 6824713155387000,3,344000,6824713155731000,0,"R",120,"[NULL]","swapper/5",0
-6824713155666000,2,78000,6824713155744000,79,"S",100,64,"kworker/u17:2",14944
-6824713155731000,3,19000,6824713155750000,2,"S",100,2,"kworker/u17:1",1134
-6824713155732000,1,85000,6824713155817000,4,"S",120,5,"UsbFfs-worker",20308
+6824713155666000,2,78000,6824713155744000,670,"S",100,670,"kworker/u17:2",14944
+6824713155731000,3,19000,6824713155750000,630,"S",100,630,"kworker/u17:1",1134
+6824713155732000,1,85000,6824713155817000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713155744000,2,88000,6824713155832000,0,"R",120,"[NULL]","swapper/5",0
 6824713155750000,3,177000,6824713155927000,0,"R",120,"[NULL]","swapper/5",0
 6824713155817000,1,92000,6824713155909000,0,"R",120,"[NULL]","swapper/5",0
-6824713155832000,2,47000,6824713155879000,2,"S",100,2,"kworker/u17:1",1134
-6824713155879000,2,58000,6824713155937000,31,"S",120,29,"kworker/2:0",18823
-6824713155909000,1,37000,6824713155946000,4,"S",120,5,"UsbFfs-worker",20308
-6824713155927000,3,16000,6824713155943000,2,"S",100,2,"kworker/u17:1",1134
+6824713155832000,2,47000,6824713155879000,630,"S",100,630,"kworker/u17:1",1134
+6824713155879000,2,58000,6824713155937000,694,"S",120,694,"kworker/2:0",18823
+6824713155909000,1,37000,6824713155946000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713155927000,3,16000,6824713155943000,630,"S",100,630,"kworker/u17:1",1134
 6824713155937000,2,171000,6824713156108000,0,"R",120,"[NULL]","swapper/5",0
 6824713155943000,3,6823000,6824713162766000,0,"R",120,"[NULL]","swapper/5",0
 6824713155946000,1,615000,6824713156561000,0,"R",120,"[NULL]","swapper/5",0
-6824713156108000,2,34000,6824713156142000,2,"S",100,2,"kworker/u17:1",1134
+6824713156108000,2,34000,6824713156142000,630,"S",100,630,"kworker/u17:1",1134
 6824713156142000,2,27000,6824713156169000,0,"R",120,"[NULL]","swapper/5",0
-6824713156169000,2,40000,6824713156209000,2,"S",100,2,"kworker/u17:1",1134
-6824713156209000,2,45000,6824713156254000,31,"S",120,29,"kworker/2:0",18823
+6824713156169000,2,40000,6824713156209000,630,"S",100,630,"kworker/u17:1",1134
+6824713156209000,2,45000,6824713156254000,694,"S",120,694,"kworker/2:0",18823
 6824713156254000,2,364000,6824713156618000,0,"R",120,"[NULL]","swapper/5",0
-6824713156561000,1,142000,6824713156703000,4,"S",120,5,"UsbFfs-worker",20308
-6824713156618000,2,133000,6824713156751000,7,"S",120,5,"adbd",20305
+6824713156561000,1,142000,6824713156703000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713156618000,2,133000,6824713156751000,739,"S",120,739,"adbd",20305
 6824713156703000,1,3760000,6824713160463000,0,"R",120,"[NULL]","swapper/5",0
 6824713156751000,2,4509000,6824713161260000,0,"R",120,"[NULL]","swapper/5",0
-6824713160021000,0,109000,6824713160130000,35,"S",120,33,"kworker/0:5",20371
-6824713160130000,0,33314000,6824713193444000,84,"R+",120,68,"ps",20463
-6824713160463000,1,578000,6824713161041000,36,"S",111,34,"SDM_EventThread",685
+6824713160021000,0,109000,6824713160130000,743,"S",120,743,"kworker/0:5",20371
+6824713160130000,0,33314000,6824713193444000,2722,"R+",120,757,"ps",20463
+6824713160463000,1,578000,6824713161041000,786,"S",111,494,"SDM_EventThread",685
 6824713161041000,1,887000,6824713161928000,0,"R",120,"[NULL]","swapper/5",0
-6824713161260000,2,459000,6824713161719000,38,"S",120,35,"HwBinder:640_1",721
+6824713161260000,2,459000,6824713161719000,777,"S",120,493,"HwBinder:640_1",721
 6824713161719000,2,304000,6824713162023000,0,"R",120,"[NULL]","swapper/5",0
-6824713161928000,1,140000,6824713162068000,40,"S",97,35,"DispSync",676
-6824713162023000,2,447000,6824713162470000,41,"S",97,35,"app",678
+6824713161928000,1,140000,6824713162068000,771,"S",97,493,"DispSync",676
+6824713162023000,2,447000,6824713162470000,773,"S",97,493,"app",678
 6824713162068000,1,358000,6824713162426000,0,"R",120,"[NULL]","swapper/5",0
-6824713162426000,1,1725000,6824713164151000,42,"S",120,36,"ndroid.systemui",1664
+6824713162426000,1,1725000,6824713164151000,644,"S",120,644,"ndroid.systemui",1664
 6824713162470000,2,1183000,6824713163653000,0,"R",120,"[NULL]","swapper/5",0
-6824713162766000,3,32000,6824713162798000,40,"S",97,35,"DispSync",676
+6824713162766000,3,32000,6824713162798000,771,"S",97,493,"DispSync",676
 6824713162798000,3,917000,6824713163715000,0,"R",120,"[NULL]","swapper/5",0
-6824713163653000,2,751000,6824713164404000,16,"S",120,14,"kworker/u16:7",19422
-6824713163715000,3,56000,6824713163771000,6,"S",120,4,"rcu_preempt",7
+6824713163653000,2,751000,6824713164404000,702,"S",120,702,"kworker/u16:7",19422
+6824713163715000,3,56000,6824713163771000,5,"S",120,5,"rcu_preempt",7
 6824713163771000,3,581000,6824713164352000,0,"R",120,"[NULL]","swapper/5",0
 6824713164151000,1,689000,6824713164840000,0,"R",120,"[NULL]","swapper/5",0
-6824713164352000,3,251000,6824713164603000,43,"S",120,35,"Binder:640_2",675
+6824713164352000,3,251000,6824713164603000,770,"S",120,493,"Binder:640_2",675
 6824713164404000,2,820000,6824713165224000,0,"R",120,"[NULL]","swapper/5",0
 6824713164603000,3,1967000,6824713166570000,0,"R",120,"[NULL]","swapper/5",0
-6824713164840000,1,120000,6824713164960000,41,"S",97,35,"app",678
+6824713164840000,1,120000,6824713164960000,773,"S",97,493,"app",678
 6824713164960000,1,408000,6824713165368000,0,"R",120,"[NULL]","swapper/5",0
-6824713165224000,2,31000,6824713165255000,40,"S",97,35,"DispSync",676
+6824713165224000,2,31000,6824713165255000,771,"S",97,493,"DispSync",676
 6824713165255000,2,581000,6824713165836000,0,"R",120,"[NULL]","swapper/5",0
-6824713165368000,1,186000,6824713165554000,82,"S",120,5,"shell",20461
+6824713165368000,1,186000,6824713165554000,2728,"S",120,739,"shell",20461
 6824713165554000,1,1573000,6824713167127000,0,"R",120,"[NULL]","swapper/5",0
-6824713165836000,2,847000,6824713166683000,7,"R+",120,5,"adbd",20305
-6824713166570000,3,92000,6824713166662000,2,"S",100,2,"kworker/u17:1",1134
-6824713166662000,3,58000,6824713166720000,49,"S",120,42,"kworker/3:1",17791
-6824713166683000,2,227000,6824713166910000,26,"S",49,23,"sugov:0",605
-6824713166720000,3,109000,6824713166829000,4,"S",120,5,"UsbFfs-worker",20308
+6824713165836000,2,847000,6824713166683000,739,"R+",120,739,"adbd",20305
+6824713166570000,3,92000,6824713166662000,630,"S",100,630,"kworker/u17:1",1134
+6824713166662000,3,58000,6824713166720000,686,"S",120,686,"kworker/3:1",17791
+6824713166683000,2,227000,6824713166910000,482,"S",49,482,"sugov:0",605
+6824713166720000,3,109000,6824713166829000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713166829000,3,636000,6824713167465000,0,"R",120,"[NULL]","swapper/5",0
-6824713166910000,2,644000,6824713167554000,7,"S",120,5,"adbd",20305
-6824713167127000,1,100000,6824713167227000,2,"S",100,2,"kworker/u17:1",1134
+6824713166910000,2,644000,6824713167554000,739,"S",120,739,"adbd",20305
+6824713167127000,1,100000,6824713167227000,630,"S",100,630,"kworker/u17:1",1134
 6824713167227000,1,42000,6824713167269000,0,"R",120,"[NULL]","swapper/5",0
-6824713167269000,1,51000,6824713167320000,2,"S",100,2,"kworker/u17:1",1134
+6824713167269000,1,51000,6824713167320000,630,"S",100,630,"kworker/u17:1",1134
 6824713167320000,1,35000,6824713167355000,0,"R",120,"[NULL]","swapper/5",0
-6824713167355000,1,100000,6824713167455000,2,"S",100,2,"kworker/u17:1",1134
-6824713167455000,1,63000,6824713167518000,48,"S",120,41,"kworker/1:1",18800
-6824713167465000,3,195000,6824713167660000,4,"S",120,5,"UsbFfs-worker",20308
-6824713167518000,1,54000,6824713167572000,2,"S",100,2,"kworker/u17:1",1134
+6824713167355000,1,100000,6824713167455000,630,"S",100,630,"kworker/u17:1",1134
+6824713167455000,1,63000,6824713167518000,693,"S",120,693,"kworker/1:1",18800
+6824713167465000,3,195000,6824713167660000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713167518000,1,54000,6824713167572000,630,"S",100,630,"kworker/u17:1",1134
 6824713167554000,2,634000,6824713168188000,0,"R",120,"[NULL]","swapper/5",0
 6824713167572000,1,42000,6824713167614000,0,"R",120,"[NULL]","swapper/5",0
-6824713167614000,1,71000,6824713167685000,2,"S",100,2,"kworker/u17:1",1134
+6824713167614000,1,71000,6824713167685000,630,"S",100,630,"kworker/u17:1",1134
 6824713167660000,3,456000,6824713168116000,0,"R",120,"[NULL]","swapper/5",0
 6824713167685000,1,45000,6824713167730000,0,"R",120,"[NULL]","swapper/5",0
-6824713167730000,1,100000,6824713167830000,2,"S",100,2,"kworker/u17:1",1134
-6824713167830000,1,52000,6824713167882000,48,"R",120,41,"kworker/1:1",18800
-6824713167882000,1,132000,6824713168014000,26,"S",49,23,"sugov:0",605
-6824713168014000,1,125000,6824713168139000,48,"S",120,41,"kworker/1:1",18800
-6824713168116000,3,145000,6824713168261000,4,"S",120,5,"UsbFfs-worker",20308
+6824713167730000,1,100000,6824713167830000,630,"S",100,630,"kworker/u17:1",1134
+6824713167830000,1,52000,6824713167882000,693,"R",120,693,"kworker/1:1",18800
+6824713167882000,1,132000,6824713168014000,482,"S",49,482,"sugov:0",605
+6824713168014000,1,125000,6824713168139000,693,"S",120,693,"kworker/1:1",18800
+6824713168116000,3,145000,6824713168261000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713168139000,1,2254000,6824713170393000,0,"R",120,"[NULL]","swapper/5",0
-6824713168188000,2,118000,6824713168306000,7,"S",120,5,"adbd",20305
+6824713168188000,2,118000,6824713168306000,739,"S",120,739,"adbd",20305
 6824713168261000,3,1720000,6824713169981000,0,"R",120,"[NULL]","swapper/5",0
 6824713168306000,2,8737000,6824713177043000,0,"R",120,"[NULL]","swapper/5",0
-6824713169981000,3,63000,6824713170044000,6,"S",120,4,"rcu_preempt",7
+6824713169981000,3,63000,6824713170044000,5,"S",120,5,"rcu_preempt",7
 6824713170044000,3,912000,6824713170956000,0,"R",120,"[NULL]","swapper/5",0
-6824713170393000,1,203000,6824713170596000,11,"S",120,9,"rcuop/0",10
+6824713170393000,1,203000,6824713170596000,8,"S",120,8,"rcuop/0",10
 6824713170596000,1,7765000,6824713178361000,0,"R",120,"[NULL]","swapper/5",0
-6824713170956000,3,31000,6824713170987000,6,"S",120,4,"rcu_preempt",7
+6824713170956000,3,31000,6824713170987000,5,"S",120,5,"rcu_preempt",7
 6824713170987000,3,6024000,6824713177011000,0,"R",120,"[NULL]","swapper/5",0
-6824713177011000,3,31000,6824713177042000,6,"S",120,4,"rcu_preempt",7
+6824713177011000,3,31000,6824713177042000,5,"S",120,5,"rcu_preempt",7
 6824713177042000,3,653000,6824713177695000,0,"R",120,"[NULL]","swapper/5",0
-6824713177043000,2,352000,6824713177395000,16,"D",120,14,"kworker/u16:7",19422
+6824713177043000,2,352000,6824713177395000,702,"D",120,702,"kworker/u16:7",19422
 6824713177395000,2,498000,6824713177893000,0,"R",120,"[NULL]","swapper/5",0
-6824713177695000,3,144000,6824713177839000,32,"S",120,30,"smem_native_rpm",87
+6824713177695000,3,144000,6824713177839000,77,"S",120,77,"smem_native_rpm",87
 6824713177839000,3,5798000,6824713183637000,0,"R",120,"[NULL]","swapper/5",0
-6824713177893000,2,35000,6824713177928000,16,"S",120,14,"kworker/u16:7",19422
+6824713177893000,2,35000,6824713177928000,702,"S",120,702,"kworker/u16:7",19422
 6824713177928000,2,920000,6824713178848000,0,"R",120,"[NULL]","swapper/5",0
-6824713178361000,1,204000,6824713178565000,82,"S",120,5,"shell",20461
+6824713178361000,1,204000,6824713178565000,2728,"S",120,739,"shell",20461
 6824713178565000,1,1277000,6824713179842000,0,"R",120,"[NULL]","swapper/5",0
-6824713178848000,2,801000,6824713179649000,7,"S",120,5,"adbd",20305
+6824713178848000,2,801000,6824713179649000,739,"S",120,739,"adbd",20305
 6824713179649000,2,622000,6824713180271000,0,"R",120,"[NULL]","swapper/5",0
-6824713179842000,1,126000,6824713179968000,2,"S",100,2,"kworker/u17:1",1134
-6824713179968000,1,67000,6824713180035000,48,"S",120,41,"kworker/1:1",18800
+6824713179842000,1,126000,6824713179968000,630,"S",100,630,"kworker/u17:1",1134
+6824713179968000,1,67000,6824713180035000,693,"S",120,693,"kworker/1:1",18800
 6824713180035000,1,535000,6824713180570000,0,"R",120,"[NULL]","swapper/5",0
-6824713180271000,2,74000,6824713180345000,4,"S",120,5,"UsbFfs-worker",20308
+6824713180271000,2,74000,6824713180345000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713180345000,2,438000,6824713180783000,0,"R",120,"[NULL]","swapper/5",0
-6824713180570000,1,46000,6824713180616000,2,"S",100,2,"kworker/u17:1",1134
+6824713180570000,1,46000,6824713180616000,630,"S",100,630,"kworker/u17:1",1134
 6824713180616000,1,116000,6824713180732000,0,"R",120,"[NULL]","swapper/5",0
-6824713180732000,1,31000,6824713180763000,2,"S",100,2,"kworker/u17:1",1134
-6824713180763000,1,28000,6824713180791000,48,"S",120,41,"kworker/1:1",18800
-6824713180783000,2,71000,6824713180854000,4,"S",120,5,"UsbFfs-worker",20308
+6824713180732000,1,31000,6824713180763000,630,"S",100,630,"kworker/u17:1",1134
+6824713180763000,1,28000,6824713180791000,693,"S",120,693,"kworker/1:1",18800
+6824713180783000,2,71000,6824713180854000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713180791000,1,34000,6824713180825000,0,"R",120,"[NULL]","swapper/5",0
-6824713180825000,1,17000,6824713180842000,2,"S",100,2,"kworker/u17:1",1134
+6824713180825000,1,17000,6824713180842000,630,"S",100,630,"kworker/u17:1",1134
 6824713180842000,1,170000,6824713181012000,0,"R",120,"[NULL]","swapper/5",0
 6824713180854000,2,1030000,6824713181884000,0,"R",120,"[NULL]","swapper/5",0
-6824713181012000,1,44000,6824713181056000,2,"S",100,2,"kworker/u17:1",1134
+6824713181012000,1,44000,6824713181056000,630,"S",100,630,"kworker/u17:1",1134
 6824713181056000,1,406000,6824713181462000,0,"R",120,"[NULL]","swapper/5",0
-6824713181462000,1,39000,6824713181501000,2,"S",100,2,"kworker/u17:1",1134
-6824713181501000,1,44000,6824713181545000,48,"S",120,41,"kworker/1:1",18800
+6824713181462000,1,39000,6824713181501000,630,"S",100,630,"kworker/u17:1",1134
+6824713181501000,1,44000,6824713181545000,693,"S",120,693,"kworker/1:1",18800
 6824713181545000,1,414000,6824713181959000,0,"R",120,"[NULL]","swapper/5",0
-6824713181884000,2,150000,6824713182034000,4,"S",120,5,"UsbFfs-worker",20308
-6824713181959000,1,179000,6824713182138000,7,"S",120,5,"adbd",20305
+6824713181884000,2,150000,6824713182034000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713181959000,1,179000,6824713182138000,739,"S",120,739,"adbd",20305
 6824713182034000,2,6995000,6824713189029000,0,"R",120,"[NULL]","swapper/5",0
 6824713182138000,1,1920000,6824713184058000,0,"R",120,"[NULL]","swapper/5",0
-6824713183637000,3,58000,6824713183695000,6,"S",120,4,"rcu_preempt",7
+6824713183637000,3,58000,6824713183695000,5,"S",120,5,"rcu_preempt",7
 6824713183695000,3,1423000,6824713185118000,0,"R",120,"[NULL]","swapper/5",0
-6824713184058000,1,700000,6824713184758000,11,"S",120,9,"rcuop/0",10
+6824713184058000,1,700000,6824713184758000,8,"S",120,8,"rcuop/0",10
 6824713184758000,1,3775000,6824713188533000,0,"R",120,"[NULL]","swapper/5",0
-6824713185118000,3,22000,6824713185140000,6,"S",120,4,"rcu_preempt",7
+6824713185118000,3,22000,6824713185140000,5,"S",120,5,"rcu_preempt",7
 6824713185140000,3,5295000,6824713190435000,0,"R",120,"[NULL]","swapper/5",0
-6824713188533000,1,169000,6824713188702000,82,"S",120,5,"shell",20461
+6824713188533000,1,169000,6824713188702000,2728,"S",120,739,"shell",20461
 6824713188702000,1,1106000,6824713189808000,0,"R",120,"[NULL]","swapper/5",0
-6824713189029000,2,560000,6824713189589000,7,"S",120,5,"adbd",20305
+6824713189029000,2,560000,6824713189589000,739,"S",120,739,"adbd",20305
 6824713189589000,2,668000,6824713190257000,0,"R",120,"[NULL]","swapper/5",0
-6824713189808000,1,118000,6824713189926000,2,"S",100,2,"kworker/u17:1",1134
-6824713189926000,1,53000,6824713189979000,48,"S",120,41,"kworker/1:1",18800
+6824713189808000,1,118000,6824713189926000,630,"S",100,630,"kworker/u17:1",1134
+6824713189926000,1,53000,6824713189979000,693,"S",120,693,"kworker/1:1",18800
 6824713189979000,1,588000,6824713190567000,0,"R",120,"[NULL]","swapper/5",0
-6824713190257000,2,67000,6824713190324000,4,"S",120,5,"UsbFfs-worker",20308
+6824713190257000,2,67000,6824713190324000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713190324000,2,156000,6824713190480000,0,"R",120,"[NULL]","swapper/5",0
-6824713190435000,3,53000,6824713190488000,6,"S",120,4,"rcu_preempt",7
-6824713190480000,2,615000,6824713191095000,11,"S",120,9,"rcuop/0",10
+6824713190435000,3,53000,6824713190488000,5,"S",120,5,"rcu_preempt",7
+6824713190480000,2,615000,6824713191095000,8,"S",120,8,"rcuop/0",10
 6824713190488000,3,904000,6824713191392000,0,"R",120,"[NULL]","swapper/5",0
-6824713190567000,1,41000,6824713190608000,2,"S",100,2,"kworker/u17:1",1134
+6824713190567000,1,41000,6824713190608000,630,"S",100,630,"kworker/u17:1",1134
 6824713190608000,1,445000,6824713191053000,0,"R",120,"[NULL]","swapper/5",0
-6824713191053000,1,39000,6824713191092000,2,"S",100,2,"kworker/u17:1",1134
-6824713191092000,1,23000,6824713191115000,48,"R+",120,41,"kworker/1:1",18800
+6824713191053000,1,39000,6824713191092000,630,"S",100,630,"kworker/u17:1",1134
+6824713191092000,1,23000,6824713191115000,693,"R+",120,693,"kworker/1:1",18800
 6824713191095000,2,22000,6824713191117000,0,"R",120,"[NULL]","swapper/5",0
-6824713191115000,1,33000,6824713191148000,2,"S",100,2,"kworker/u17:1",1134
-6824713191117000,2,95000,6824713191212000,4,"S",120,5,"UsbFfs-worker",20308
-6824713191148000,1,15000,6824713191163000,48,"S",120,41,"kworker/1:1",18800
+6824713191115000,1,33000,6824713191148000,630,"S",100,630,"kworker/u17:1",1134
+6824713191117000,2,95000,6824713191212000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713191148000,1,15000,6824713191163000,693,"S",120,693,"kworker/1:1",18800
 6824713191163000,1,41000,6824713191204000,0,"R",120,"[NULL]","swapper/5",0
-6824713191204000,1,40000,6824713191244000,2,"S",100,2,"kworker/u17:1",1134
+6824713191204000,1,40000,6824713191244000,630,"S",100,630,"kworker/u17:1",1134
 6824713191212000,2,58000,6824713191270000,0,"R",120,"[NULL]","swapper/5",0
-6824713191244000,1,41000,6824713191285000,48,"S",120,41,"kworker/1:1",18800
-6824713191270000,2,141000,6824713191411000,4,"S",120,5,"UsbFfs-worker",20308
+6824713191244000,1,41000,6824713191285000,693,"S",120,693,"kworker/1:1",18800
+6824713191270000,2,141000,6824713191411000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713191285000,1,50000,6824713191335000,0,"R",120,"[NULL]","swapper/5",0
-6824713191335000,1,184000,6824713191519000,7,"S",120,5,"adbd",20305
-6824713191392000,3,26000,6824713191418000,6,"S",120,4,"rcu_preempt",7
+6824713191335000,1,184000,6824713191519000,739,"S",120,739,"adbd",20305
+6824713191392000,3,26000,6824713191418000,5,"S",120,5,"rcu_preempt",7
 6824713191411000,2,3384000,6824713194795000,0,"R",120,"[NULL]","swapper/5",0
 6824713191418000,3,4124000,6824713195542000,0,"R",120,"[NULL]","swapper/5",0
 6824713191519000,1,2312000,6824713193831000,0,"R",120,"[NULL]","swapper/5",0
-6824713193444000,0,76000,6824713193520000,35,"S",120,33,"kworker/0:5",20371
-6824713193520000,0,3109000,6824713196629000,84,"R+",120,68,"ps",20463
-6824713193831000,1,657000,6824713194488000,36,"S",111,34,"SDM_EventThread",685
+6824713193444000,0,76000,6824713193520000,743,"S",120,743,"kworker/0:5",20371
+6824713193520000,0,3109000,6824713196629000,2722,"R+",120,757,"ps",20463
+6824713193831000,1,657000,6824713194488000,786,"S",111,494,"SDM_EventThread",685
 6824713194488000,1,600000,6824713195088000,0,"R",120,"[NULL]","swapper/5",0
-6824713194795000,2,411000,6824713195206000,38,"S",120,35,"HwBinder:640_1",721
-6824713195088000,1,141000,6824713195229000,40,"S",97,35,"DispSync",676
+6824713194795000,2,411000,6824713195206000,777,"S",120,493,"HwBinder:640_1",721
+6824713195088000,1,141000,6824713195229000,771,"S",97,493,"DispSync",676
 6824713195206000,2,990000,6824713196196000,0,"R",120,"[NULL]","swapper/5",0
 6824713195229000,1,889000,6824713196118000,0,"R",120,"[NULL]","swapper/5",0
-6824713195542000,3,272000,6824713195814000,41,"S",97,35,"app",678
+6824713195542000,3,272000,6824713195814000,773,"S",97,493,"app",678
 6824713195814000,3,811000,6824713196625000,0,"R",120,"[NULL]","swapper/5",0
-6824713196118000,1,2042000,6824713198160000,42,"S",120,36,"ndroid.systemui",1664
-6824713196196000,2,19000,6824713196215000,40,"S",97,35,"DispSync",676
+6824713196118000,1,2042000,6824713198160000,644,"S",120,644,"ndroid.systemui",1664
+6824713196196000,2,19000,6824713196215000,771,"S",97,493,"DispSync",676
 6824713196215000,2,3119000,6824713199334000,0,"R",120,"[NULL]","swapper/5",0
-6824713196625000,3,44000,6824713196669000,6,"S",120,4,"rcu_preempt",7
-6824713196629000,0,168000,6824713196797000,26,"S",49,23,"sugov:0",605
+6824713196625000,3,44000,6824713196669000,5,"S",120,5,"rcu_preempt",7
+6824713196629000,0,168000,6824713196797000,482,"S",49,482,"sugov:0",605
 6824713196669000,3,1654000,6824713198323000,0,"R",120,"[NULL]","swapper/5",0
-6824713196797000,0,30115000,6824713226912000,84,"R+",120,68,"ps",20463
+6824713196797000,0,30115000,6824713226912000,2722,"R+",120,757,"ps",20463
 6824713198160000,1,730000,6824713198890000,0,"R",120,"[NULL]","swapper/5",0
-6824713198323000,3,309000,6824713198632000,43,"S",120,35,"Binder:640_2",675
+6824713198323000,3,309000,6824713198632000,770,"S",120,493,"Binder:640_2",675
 6824713198632000,3,5040000,6824713203672000,0,"R",120,"[NULL]","swapper/5",0
-6824713198890000,1,161000,6824713199051000,41,"S",97,35,"app",678
+6824713198890000,1,161000,6824713199051000,773,"S",97,493,"app",678
 6824713199051000,1,29000,6824713199080000,0,"R",120,"[NULL]","swapper/5",0
-6824713199080000,1,118000,6824713199198000,26,"S",49,23,"sugov:0",605
+6824713199080000,1,118000,6824713199198000,482,"S",49,482,"sugov:0",605
 6824713199198000,1,5504000,6824713204702000,0,"R",120,"[NULL]","swapper/5",0
-6824713199334000,2,29000,6824713199363000,40,"S",97,35,"DispSync",676
+6824713199334000,2,29000,6824713199363000,771,"S",97,493,"DispSync",676
 6824713199363000,2,4705000,6824713204068000,0,"R",120,"[NULL]","swapper/5",0
-6824713203672000,3,74000,6824713203746000,6,"S",120,4,"rcu_preempt",7
+6824713203672000,3,74000,6824713203746000,5,"S",120,5,"rcu_preempt",7
 6824713203746000,3,1079000,6824713204825000,0,"R",120,"[NULL]","swapper/5",0
-6824713204068000,2,436000,6824713204504000,11,"S",120,9,"rcuop/0",10
+6824713204068000,2,436000,6824713204504000,8,"S",120,8,"rcuop/0",10
 6824713204504000,2,743000,6824713205247000,0,"R",120,"[NULL]","swapper/5",0
-6824713204702000,1,215000,6824713204917000,82,"S",120,5,"shell",20461
-6824713204825000,3,28000,6824713204853000,6,"S",120,4,"rcu_preempt",7
+6824713204702000,1,215000,6824713204917000,2728,"S",120,739,"shell",20461
+6824713204825000,3,28000,6824713204853000,5,"S",120,5,"rcu_preempt",7
 6824713204853000,3,1654000,6824713206507000,0,"R",120,"[NULL]","swapper/5",0
 6824713204917000,1,1067000,6824713205984000,0,"R",120,"[NULL]","swapper/5",0
-6824713205247000,2,966000,6824713206213000,7,"S",120,5,"adbd",20305
-6824713205984000,1,70000,6824713206054000,2,"S",100,2,"kworker/u17:1",1134
+6824713205247000,2,966000,6824713206213000,739,"S",120,739,"adbd",20305
+6824713205984000,1,70000,6824713206054000,630,"S",100,630,"kworker/u17:1",1134
 6824713206054000,1,9000,6824713206063000,0,"R",120,"[NULL]","swapper/5",0
-6824713206063000,1,59000,6824713206122000,2,"S",100,2,"kworker/u17:1",1134
-6824713206122000,1,57000,6824713206179000,48,"S",120,41,"kworker/1:1",18800
-6824713206179000,1,100000,6824713206279000,4,"S",120,5,"UsbFfs-worker",20308
+6824713206063000,1,59000,6824713206122000,630,"S",100,630,"kworker/u17:1",1134
+6824713206122000,1,57000,6824713206179000,693,"S",120,693,"kworker/1:1",18800
+6824713206179000,1,100000,6824713206279000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713206213000,2,904000,6824713207117000,0,"R",120,"[NULL]","swapper/5",0
 6824713206279000,1,1131000,6824713207410000,0,"R",120,"[NULL]","swapper/5",0
-6824713206507000,3,43000,6824713206550000,2,"S",100,2,"kworker/u17:1",1134
+6824713206507000,3,43000,6824713206550000,630,"S",100,630,"kworker/u17:1",1134
 6824713206550000,3,436000,6824713206986000,0,"R",120,"[NULL]","swapper/5",0
-6824713206986000,3,67000,6824713207053000,2,"S",100,2,"kworker/u17:1",1134
-6824713207053000,3,47000,6824713207100000,49,"S",120,42,"kworker/3:1",17791
+6824713206986000,3,67000,6824713207053000,630,"S",100,630,"kworker/u17:1",1134
+6824713207053000,3,47000,6824713207100000,686,"S",120,686,"kworker/3:1",17791
 6824713207100000,3,2871000,6824713209971000,0,"R",120,"[NULL]","swapper/5",0
-6824713207117000,2,57000,6824713207174000,2,"S",100,2,"kworker/u17:1",1134
-6824713207174000,2,56000,6824713207230000,31,"S",120,29,"kworker/2:0",18823
+6824713207117000,2,57000,6824713207174000,630,"S",100,630,"kworker/u17:1",1134
+6824713207174000,2,56000,6824713207230000,694,"S",120,694,"kworker/2:0",18823
 6824713207230000,2,623000,6824713207853000,0,"R",120,"[NULL]","swapper/5",0
-6824713207410000,1,204000,6824713207614000,4,"S",120,5,"UsbFfs-worker",20308
+6824713207410000,1,204000,6824713207614000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713207614000,1,16701000,6824713224315000,0,"R",120,"[NULL]","swapper/5",0
-6824713207853000,2,179000,6824713208032000,7,"S",120,5,"adbd",20305
+6824713207853000,2,179000,6824713208032000,739,"S",120,739,"adbd",20305
 6824713208032000,2,2366000,6824713210398000,0,"R",120,"[NULL]","swapper/5",0
-6824713209971000,3,55000,6824713210026000,6,"S",120,4,"rcu_preempt",7
+6824713209971000,3,55000,6824713210026000,5,"S",120,5,"rcu_preempt",7
 6824713210026000,3,1318000,6824713211344000,0,"R",120,"[NULL]","swapper/5",0
-6824713210398000,2,635000,6824713211033000,11,"S",120,9,"rcuop/0",10
+6824713210398000,2,635000,6824713211033000,8,"S",120,8,"rcuop/0",10
 6824713211033000,2,6391000,6824713217424000,0,"R",120,"[NULL]","swapper/5",0
-6824713211344000,3,26000,6824713211370000,6,"S",120,4,"rcu_preempt",7
+6824713211344000,3,26000,6824713211370000,5,"S",120,5,"rcu_preempt",7
 6824713211370000,3,5656000,6824713217026000,0,"R",120,"[NULL]","swapper/5",0
-6824713217026000,3,51000,6824713217077000,6,"S",120,4,"rcu_preempt",7
+6824713217026000,3,51000,6824713217077000,5,"S",120,5,"rcu_preempt",7
 6824713217077000,3,770000,6824713217847000,0,"R",120,"[NULL]","swapper/5",0
-6824713217424000,2,75000,6824713217499000,11,"S",120,9,"rcuop/0",10
+6824713217424000,2,75000,6824713217499000,8,"S",120,8,"rcuop/0",10
 6824713217499000,2,6882000,6824713224381000,0,"R",120,"[NULL]","swapper/5",0
-6824713217847000,3,19000,6824713217866000,6,"S",120,4,"rcu_preempt",7
+6824713217847000,3,19000,6824713217866000,5,"S",120,5,"rcu_preempt",7
 6824713217866000,3,6087000,6824713223953000,0,"R",120,"[NULL]","swapper/5",0
-6824713223953000,3,87000,6824713224040000,6,"S",120,4,"rcu_preempt",7
+6824713223953000,3,87000,6824713224040000,5,"S",120,5,"rcu_preempt",7
 6824713224040000,3,879000,6824713224919000,0,"R",120,"[NULL]","swapper/5",0
-6824713224315000,1,256000,6824713224571000,82,"S",120,5,"shell",20461
-6824713224381000,2,258000,6824713224639000,11,"S",120,9,"rcuop/0",10
+6824713224315000,1,256000,6824713224571000,2728,"S",120,739,"shell",20461
+6824713224381000,2,258000,6824713224639000,8,"S",120,8,"rcuop/0",10
 6824713224571000,1,589000,6824713225160000,0,"R",120,"[NULL]","swapper/5",0
 6824713224639000,2,1469000,6824713226108000,0,"R",120,"[NULL]","swapper/5",0
-6824713224919000,3,932000,6824713225851000,7,"S",120,5,"adbd",20305
-6824713225160000,1,33000,6824713225193000,6,"S",120,4,"rcu_preempt",7
+6824713224919000,3,932000,6824713225851000,739,"S",120,739,"adbd",20305
+6824713225160000,1,33000,6824713225193000,5,"S",120,5,"rcu_preempt",7
 6824713225193000,1,2096000,6824713227289000,0,"R",120,"[NULL]","swapper/5",0
 6824713225851000,3,755000,6824713226606000,0,"R",120,"[NULL]","swapper/5",0
-6824713226108000,2,128000,6824713226236000,2,"S",100,2,"kworker/u17:1",1134
-6824713226236000,2,76000,6824713226312000,31,"S",120,29,"kworker/2:0",18823
+6824713226108000,2,128000,6824713226236000,630,"S",100,630,"kworker/u17:1",1134
+6824713226236000,2,76000,6824713226312000,694,"S",120,694,"kworker/2:0",18823
 6824713226312000,2,35000,6824713226347000,0,"R",120,"[NULL]","swapper/5",0
-6824713226347000,2,23000,6824713226370000,2,"S",100,2,"kworker/u17:1",1134
-6824713226370000,2,20000,6824713226390000,31,"S",120,29,"kworker/2:0",18823
+6824713226347000,2,23000,6824713226370000,630,"S",100,630,"kworker/u17:1",1134
+6824713226370000,2,20000,6824713226390000,694,"S",120,694,"kworker/2:0",18823
 6824713226390000,2,562000,6824713226952000,0,"R",120,"[NULL]","swapper/5",0
-6824713226606000,3,144000,6824713226750000,4,"S",120,5,"UsbFfs-worker",20308
+6824713226606000,3,144000,6824713226750000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713226750000,3,199000,6824713226949000,0,"R",120,"[NULL]","swapper/5",0
-6824713226912000,0,65000,6824713226977000,35,"S",120,33,"kworker/0:5",20371
-6824713226949000,3,43000,6824713226992000,2,"S",100,2,"kworker/u17:1",1134
-6824713226952000,2,113000,6824713227065000,16,"S",120,14,"kworker/u16:7",19422
-6824713226977000,0,23808000,6824713250785000,84,"R",120,68,"ps",20463
+6824713226912000,0,65000,6824713226977000,743,"S",120,743,"kworker/0:5",20371
+6824713226949000,3,43000,6824713226992000,630,"S",100,630,"kworker/u17:1",1134
+6824713226952000,2,113000,6824713227065000,702,"S",120,702,"kworker/u16:7",19422
+6824713226977000,0,23808000,6824713250785000,2722,"R",120,757,"ps",20463
 6824713226992000,3,43000,6824713227035000,0,"R",120,"[NULL]","swapper/5",0
-6824713227035000,3,61000,6824713227096000,2,"S",100,2,"kworker/u17:1",1134
+6824713227035000,3,61000,6824713227096000,630,"S",100,630,"kworker/u17:1",1134
 6824713227065000,2,78000,6824713227143000,0,"R",120,"[NULL]","swapper/5",0
-6824713227096000,3,66000,6824713227162000,49,"S",120,42,"kworker/3:1",17791
-6824713227143000,2,162000,6824713227305000,4,"S",120,5,"UsbFfs-worker",20308
+6824713227096000,3,66000,6824713227162000,686,"S",120,686,"kworker/3:1",17791
+6824713227143000,2,162000,6824713227305000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713227162000,3,446000,6824713227608000,0,"R",120,"[NULL]","swapper/5",0
-6824713227289000,1,566000,6824713227855000,36,"S",111,34,"SDM_EventThread",685
+6824713227289000,1,566000,6824713227855000,786,"S",111,494,"SDM_EventThread",685
 6824713227305000,2,440000,6824713227745000,0,"R",120,"[NULL]","swapper/5",0
-6824713227608000,3,224000,6824713227832000,7,"S",120,5,"adbd",20305
-6824713227745000,2,443000,6824713228188000,38,"S",120,35,"HwBinder:640_1",721
+6824713227608000,3,224000,6824713227832000,739,"S",120,739,"adbd",20305
+6824713227745000,2,443000,6824713228188000,777,"S",120,493,"HwBinder:640_1",721
 6824713227832000,3,1635000,6824713229467000,0,"R",120,"[NULL]","swapper/5",0
 6824713227855000,1,548000,6824713228403000,0,"R",120,"[NULL]","swapper/5",0
 6824713228188000,2,681000,6824713228869000,0,"R",120,"[NULL]","swapper/5",0
-6824713228403000,1,135000,6824713228538000,40,"S",97,35,"DispSync",676
+6824713228403000,1,135000,6824713228538000,771,"S",97,493,"DispSync",676
 6824713228538000,1,565000,6824713229103000,0,"R",120,"[NULL]","swapper/5",0
-6824713228869000,2,274000,6824713229143000,41,"S",97,35,"app",678
-6824713229103000,1,1753000,6824713230856000,42,"S",120,36,"ndroid.systemui",1664
+6824713228869000,2,274000,6824713229143000,773,"S",97,493,"app",678
+6824713229103000,1,1753000,6824713230856000,644,"S",120,644,"ndroid.systemui",1664
 6824713229143000,2,1167000,6824713230310000,0,"R",120,"[NULL]","swapper/5",0
-6824713229467000,3,23000,6824713229490000,40,"S",97,35,"DispSync",676
+6824713229467000,3,23000,6824713229490000,771,"S",97,493,"DispSync",676
 6824713229490000,3,1513000,6824713231003000,0,"R",120,"[NULL]","swapper/5",0
-6824713230310000,2,45000,6824713230355000,6,"S",120,4,"rcu_preempt",7
+6824713230310000,2,45000,6824713230355000,5,"S",120,5,"rcu_preempt",7
 6824713230355000,2,1542000,6824713231897000,0,"R",120,"[NULL]","swapper/5",0
 6824713230856000,1,642000,6824713231498000,0,"R",120,"[NULL]","swapper/5",0
-6824713231003000,3,258000,6824713231261000,43,"S",120,35,"Binder:640_2",675
+6824713231003000,3,258000,6824713231261000,770,"S",120,493,"Binder:640_2",675
 6824713231261000,3,5466000,6824713236727000,0,"R",120,"[NULL]","swapper/5",0
-6824713231498000,1,138000,6824713231636000,41,"S",97,35,"app",678
+6824713231498000,1,138000,6824713231636000,773,"S",97,493,"app",678
 6824713231636000,1,6415000,6824713238051000,0,"R",120,"[NULL]","swapper/5",0
-6824713231897000,2,28000,6824713231925000,40,"S",97,35,"DispSync",676
+6824713231897000,2,28000,6824713231925000,771,"S",97,493,"DispSync",676
 6824713231925000,2,4745000,6824713236670000,0,"R",120,"[NULL]","swapper/5",0
-6824713236670000,2,70000,6824713236740000,6,"S",120,4,"rcu_preempt",7
-6824713236727000,3,267000,6824713236994000,11,"S",120,9,"rcuop/0",10
+6824713236670000,2,70000,6824713236740000,5,"S",120,5,"rcu_preempt",7
+6824713236727000,3,267000,6824713236994000,8,"S",120,8,"rcuop/0",10
 6824713236740000,2,246000,6824713236986000,0,"R",120,"[NULL]","swapper/5",0
-6824713236986000,2,17000,6824713237003000,6,"S",120,4,"rcu_preempt",7
+6824713236986000,2,17000,6824713237003000,5,"S",120,5,"rcu_preempt",7
 6824713236994000,3,1550000,6824713238544000,0,"R",120,"[NULL]","swapper/5",0
 6824713237003000,2,3473000,6824713240476000,0,"R",120,"[NULL]","swapper/5",0
-6824713238051000,1,210000,6824713238261000,82,"S",120,5,"shell",20461
+6824713238051000,1,210000,6824713238261000,2728,"S",120,739,"shell",20461
 6824713238261000,1,7678000,6824713245939000,0,"R",120,"[NULL]","swapper/5",0
-6824713238544000,3,824000,6824713239368000,7,"S",120,5,"adbd",20305
+6824713238544000,3,824000,6824713239368000,739,"S",120,739,"adbd",20305
 6824713239368000,3,631000,6824713239999000,0,"R",120,"[NULL]","swapper/5",0
-6824713239999000,3,127000,6824713240126000,2,"S",100,2,"kworker/u17:1",1134
-6824713240126000,3,61000,6824713240187000,49,"S",120,42,"kworker/3:1",17791
+6824713239999000,3,127000,6824713240126000,630,"S",100,630,"kworker/u17:1",1134
+6824713240126000,3,61000,6824713240187000,686,"S",120,686,"kworker/3:1",17791
 6824713240187000,3,594000,6824713240781000,0,"R",120,"[NULL]","swapper/5",0
-6824713240476000,2,102000,6824713240578000,4,"S",120,5,"UsbFfs-worker",20308
+6824713240476000,2,102000,6824713240578000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713240578000,2,560000,6824713241138000,0,"R",120,"[NULL]","swapper/5",0
-6824713240781000,3,38000,6824713240819000,2,"S",100,2,"kworker/u17:1",1134
-6824713240819000,3,32000,6824713240851000,49,"S",120,42,"kworker/3:1",17791
+6824713240781000,3,38000,6824713240819000,630,"S",100,630,"kworker/u17:1",1134
+6824713240819000,3,32000,6824713240851000,686,"S",120,686,"kworker/3:1",17791
 6824713240851000,3,284000,6824713241135000,0,"R",120,"[NULL]","swapper/5",0
-6824713241135000,3,35000,6824713241170000,2,"S",100,2,"kworker/u17:1",1134
-6824713241138000,2,73000,6824713241211000,4,"S",120,5,"UsbFfs-worker",20308
+6824713241135000,3,35000,6824713241170000,630,"S",100,630,"kworker/u17:1",1134
+6824713241138000,2,73000,6824713241211000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713241170000,3,27000,6824713241197000,0,"R",120,"[NULL]","swapper/5",0
-6824713241197000,3,40000,6824713241237000,2,"S",100,2,"kworker/u17:1",1134
+6824713241197000,3,40000,6824713241237000,630,"S",100,630,"kworker/u17:1",1134
 6824713241211000,2,447000,6824713241658000,0,"R",120,"[NULL]","swapper/5",0
-6824713241237000,3,43000,6824713241280000,49,"S",120,42,"kworker/3:1",17791
+6824713241237000,3,43000,6824713241280000,686,"S",120,686,"kworker/3:1",17791
 6824713241280000,3,763000,6824713242043000,0,"R",120,"[NULL]","swapper/5",0
-6824713241658000,2,183000,6824713241841000,4,"S",120,5,"UsbFfs-worker",20308
+6824713241658000,2,183000,6824713241841000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713241841000,2,1454000,6824713243295000,0,"R",120,"[NULL]","swapper/5",0
-6824713242043000,3,215000,6824713242258000,7,"S",120,5,"adbd",20305
+6824713242043000,3,215000,6824713242258000,739,"S",120,739,"adbd",20305
 6824713242258000,3,5163000,6824713247421000,0,"R",120,"[NULL]","swapper/5",0
-6824713243295000,2,43000,6824713243338000,6,"S",120,4,"rcu_preempt",7
+6824713243295000,2,43000,6824713243338000,5,"S",120,5,"rcu_preempt",7
 6824713243338000,2,3652000,6824713246990000,0,"R",120,"[NULL]","swapper/5",0
-6824713245939000,1,86000,6824713246025000,57,"S",120,46,"ogle.android.as",15166
-6824713246025000,1,64000,6824713246089000,56,"S",120,46,"ogle.android.as",15167
+6824713245939000,1,86000,6824713246025000,1807,"S",120,663,"ogle.android.as",15166
+6824713246025000,1,64000,6824713246089000,1808,"S",120,663,"ogle.android.as",15167
 6824713246089000,1,9699000,6824713255788000,0,"R",120,"[NULL]","swapper/5",0
-6824713246990000,2,51000,6824713247041000,6,"S",120,4,"rcu_preempt",7
+6824713246990000,2,51000,6824713247041000,5,"S",120,5,"rcu_preempt",7
 6824713247041000,2,1235000,6824713248276000,0,"R",120,"[NULL]","swapper/5",0
-6824713247421000,3,487000,6824713247908000,11,"S",120,9,"rcuop/0",10
+6824713247421000,3,487000,6824713247908000,8,"S",120,8,"rcuop/0",10
 6824713247908000,3,4863000,6824713252771000,0,"R",120,"[NULL]","swapper/5",0
-6824713248276000,2,25000,6824713248301000,6,"S",120,4,"rcu_preempt",7
+6824713248276000,2,25000,6824713248301000,5,"S",120,5,"rcu_preempt",7
 6824713248301000,2,4882000,6824713253183000,0,"R",120,"[NULL]","swapper/5",0
-6824713250362000,6,413000,6824713250775000,82,"S",120,5,"shell",20461
+6824713250362000,6,413000,6824713250775000,2728,"S",120,739,"shell",20461
 6824713250775000,6,72000,6824713250847000,0,"R",120,"[NULL]","swapper/5",0
-6824713250785000,0,74000,6824713250859000,86,"S",0,70,"migration/0",13
-6824713250847000,6,2556000,6824713253403000,84,"R",120,68,"ps",20463
+6824713250785000,0,74000,6824713250859000,11,"S",0,11,"migration/0",13
+6824713250847000,6,2556000,6824713253403000,2722,"R",120,757,"ps",20463
 6824713250859000,0,61000,6824713250920000,0,"R",120,"[NULL]","swapper/5",0
-6824713250920000,0,247000,6824713251167000,26,"S",49,23,"sugov:0",605
-6824713251148000,7,902000,6824713252050000,7,"S",120,5,"adbd",20305
+6824713250920000,0,247000,6824713251167000,482,"S",49,482,"sugov:0",605
+6824713251148000,7,902000,6824713252050000,739,"S",120,739,"adbd",20305
 6824713251167000,0,342000,6824713251509000,0,"R",120,"[NULL]","swapper/5",0
-6824713251509000,0,58000,6824713251567000,8,"S",120,8,"rcuop/6",60
+6824713251509000,0,58000,6824713251567000,52,"S",120,52,"rcuop/6",60
 6824713251567000,0,7123000,6824713258690000,0,"R",120,"[NULL]","swapper/5",0
 6824713252050000,7,3110000,6824713255160000,0,"R",120,"[NULL]","swapper/5",0
-6824713252771000,3,197000,6824713252968000,2,"S",100,2,"kworker/u17:1",1134
-6824713252968000,3,186000,6824713253154000,49,"S",120,42,"kworker/3:1",17791
-6824713253154000,3,188000,6824713253342000,2,"S",100,2,"kworker/u17:1",1134
-6824713253183000,2,568000,6824713253751000,4,"S",120,5,"UsbFfs-worker",20308
-6824713253342000,3,68000,6824713253410000,49,"S",120,42,"kworker/3:1",17791
-6824713253403000,6,156000,6824713253559000,22,"S",49,20,"sugov:4",606
-6824713253410000,3,69000,6824713253479000,2,"S",100,2,"kworker/u17:1",1134
+6824713252771000,3,197000,6824713252968000,630,"S",100,630,"kworker/u17:1",1134
+6824713252968000,3,186000,6824713253154000,686,"S",120,686,"kworker/3:1",17791
+6824713253154000,3,188000,6824713253342000,630,"S",100,630,"kworker/u17:1",1134
+6824713253183000,2,568000,6824713253751000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713253342000,3,68000,6824713253410000,686,"S",120,686,"kworker/3:1",17791
+6824713253403000,6,156000,6824713253559000,483,"S",49,483,"sugov:4",606
+6824713253410000,3,69000,6824713253479000,630,"S",100,630,"kworker/u17:1",1134
 6824713253479000,3,642000,6824713254121000,0,"R",120,"[NULL]","swapper/5",0
-6824713253559000,6,223000,6824713253782000,84,"R+",120,68,"ps",20463
-6824713253751000,2,72000,6824713253823000,6,"S",120,4,"rcu_preempt",7
-6824713253782000,6,16000,6824713253798000,82,"S",120,5,"shell",20461
-6824713253798000,6,1208000,6824713255006000,84,"x",120,68,"ps",20463
+6824713253559000,6,223000,6824713253782000,2722,"R+",120,757,"ps",20463
+6824713253751000,2,72000,6824713253823000,5,"S",120,5,"rcu_preempt",7
+6824713253782000,6,16000,6824713253798000,2728,"S",120,739,"shell",20461
+6824713253798000,6,1208000,6824713255006000,2722,"x",120,757,"ps",20463
 6824713253823000,2,1690000,6824713255513000,0,"R",120,"[NULL]","swapper/5",0
-6824713254121000,3,94000,6824713254215000,2,"S",100,2,"kworker/u17:1",1134
+6824713254121000,3,94000,6824713254215000,630,"S",100,630,"kworker/u17:1",1134
 6824713254215000,3,534000,6824713254749000,0,"R",120,"[NULL]","swapper/5",0
-6824713254749000,3,113000,6824713254862000,2,"S",100,2,"kworker/u17:1",1134
-6824713254862000,3,213000,6824713255075000,49,"S",120,42,"kworker/3:1",17791
+6824713254749000,3,113000,6824713254862000,630,"S",100,630,"kworker/u17:1",1134
+6824713254862000,3,213000,6824713255075000,686,"S",120,686,"kworker/3:1",17791
 6824713255006000,6,74037000,6824713329043000,0,"R",120,"[NULL]","swapper/5",0
 6824713255075000,3,5496000,6824713260571000,0,"R",120,"[NULL]","swapper/5",0
-6824713255160000,7,39000,6824713255199000,28,"S",120,26,"rcuos/6",61
-6824713255199000,7,23000,6824713255222000,5,"S",120,7,"rcu_sched",8
+6824713255160000,7,39000,6824713255199000,53,"S",120,53,"rcuos/6",61
+6824713255199000,7,23000,6824713255222000,6,"S",120,6,"rcu_sched",8
 6824713255222000,7,6211000,6824713261433000,0,"R",120,"[NULL]","swapper/5",0
-6824713255513000,2,403000,6824713255916000,4,"S",120,5,"UsbFfs-worker",20308
-6824713255788000,1,140000,6824713255928000,26,"S",49,23,"sugov:0",605
-6824713255916000,2,1029000,6824713256945000,7,"R",120,5,"adbd",20305
-6824713255928000,1,733000,6824713256661000,83,"R+",120,67,"sh",20462
-6824713256661000,1,87000,6824713256748000,26,"S",49,23,"sugov:0",605
-6824713256748000,1,2388000,6824713259136000,83,"S",120,67,"sh",20462
-6824713256945000,2,173000,6824713257118000,2,"S",100,2,"kworker/u17:1",1134
-6824713257118000,2,59000,6824713257177000,31,"R+",120,29,"kworker/2:0",18823
-6824713257177000,2,41000,6824713257218000,2,"S",100,2,"kworker/u17:1",1134
-6824713257218000,2,23000,6824713257241000,79,"S",100,64,"kworker/u17:2",14944
-6824713257241000,2,60000,6824713257301000,31,"R+",120,29,"kworker/2:0",18823
-6824713257301000,2,26000,6824713257327000,79,"S",100,64,"kworker/u17:2",14944
-6824713257327000,2,72000,6824713257399000,31,"S",120,29,"kworker/2:0",18823
-6824713257399000,2,76000,6824713257475000,4,"R",120,5,"UsbFfs-worker",20308
-6824713257475000,2,44000,6824713257519000,79,"S",100,64,"kworker/u17:2",14944
-6824713257519000,2,62000,6824713257581000,4,"R",120,5,"UsbFfs-worker",20308
-6824713257581000,2,65000,6824713257646000,79,"S",100,64,"kworker/u17:2",14944
-6824713257646000,2,44000,6824713257690000,31,"S",120,29,"kworker/2:0",18823
-6824713257690000,2,127000,6824713257817000,7,"S",120,5,"adbd",20305
-6824713257817000,2,122000,6824713257939000,4,"R+",120,5,"UsbFfs-worker",20308
-6824713257939000,2,73000,6824713258012000,26,"S",49,23,"sugov:0",605
-6824713258012000,2,131000,6824713258143000,7,"S",120,5,"adbd",20305
-6824713258143000,2,148000,6824713258291000,4,"S",120,5,"UsbFfs-worker",20308
+6824713255513000,2,403000,6824713255916000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713255788000,1,140000,6824713255928000,482,"S",49,482,"sugov:0",605
+6824713255916000,2,1029000,6824713256945000,739,"R",120,739,"adbd",20305
+6824713255928000,1,733000,6824713256661000,2721,"R+",120,756,"sh",20462
+6824713256661000,1,87000,6824713256748000,482,"S",49,482,"sugov:0",605
+6824713256748000,1,2388000,6824713259136000,2721,"S",120,756,"sh",20462
+6824713256945000,2,173000,6824713257118000,630,"S",100,630,"kworker/u17:1",1134
+6824713257118000,2,59000,6824713257177000,694,"R+",120,694,"kworker/2:0",18823
+6824713257177000,2,41000,6824713257218000,630,"S",100,630,"kworker/u17:1",1134
+6824713257218000,2,23000,6824713257241000,670,"S",100,670,"kworker/u17:2",14944
+6824713257241000,2,60000,6824713257301000,694,"R+",120,694,"kworker/2:0",18823
+6824713257301000,2,26000,6824713257327000,670,"S",100,670,"kworker/u17:2",14944
+6824713257327000,2,72000,6824713257399000,694,"S",120,694,"kworker/2:0",18823
+6824713257399000,2,76000,6824713257475000,2487,"R",120,739,"UsbFfs-worker",20308
+6824713257475000,2,44000,6824713257519000,670,"S",100,670,"kworker/u17:2",14944
+6824713257519000,2,62000,6824713257581000,2487,"R",120,739,"UsbFfs-worker",20308
+6824713257581000,2,65000,6824713257646000,670,"S",100,670,"kworker/u17:2",14944
+6824713257646000,2,44000,6824713257690000,694,"S",120,694,"kworker/2:0",18823
+6824713257690000,2,127000,6824713257817000,739,"S",120,739,"adbd",20305
+6824713257817000,2,122000,6824713257939000,2487,"R+",120,739,"UsbFfs-worker",20308
+6824713257939000,2,73000,6824713258012000,482,"S",49,482,"sugov:0",605
+6824713258012000,2,131000,6824713258143000,739,"S",120,739,"adbd",20305
+6824713258143000,2,148000,6824713258291000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713258291000,2,1744000,6824713260035000,0,"R",120,"[NULL]","swapper/5",0
-6824713258690000,0,164000,6824713258854000,7,"S",120,5,"adbd",20305
+6824713258690000,0,164000,6824713258854000,739,"S",120,739,"adbd",20305
 6824713258854000,0,1935000,6824713260789000,0,"R",120,"[NULL]","swapper/5",0
-6824713259136000,1,895000,6824713260031000,87,"R+",120,71,"ps",20464
-6824713260031000,1,69000,6824713260100000,48,"S",120,41,"kworker/1:1",18800
-6824713260035000,2,103000,6824713260138000,6,"S",120,4,"rcu_preempt",7
-6824713260100000,1,747000,6824713260847000,87,"R+",120,71,"ps",20464
+6824713259136000,1,895000,6824713260031000,2729,"R+",120,758,"ps",20464
+6824713260031000,1,69000,6824713260100000,693,"S",120,693,"kworker/1:1",18800
+6824713260035000,2,103000,6824713260138000,5,"S",120,5,"rcu_preempt",7
+6824713260100000,1,747000,6824713260847000,2729,"R+",120,758,"ps",20464
 6824713260138000,2,1298000,6824713261436000,0,"R",120,"[NULL]","swapper/5",0
-6824713260571000,3,513000,6824713261084000,11,"S",120,9,"rcuop/0",10
-6824713260789000,0,106000,6824713260895000,35,"S",120,33,"kworker/0:5",20371
-6824713260847000,1,692000,6824713261539000,36,"S",111,34,"SDM_EventThread",685
+6824713260571000,3,513000,6824713261084000,8,"S",120,8,"rcuop/0",10
+6824713260789000,0,106000,6824713260895000,743,"S",120,743,"kworker/0:5",20371
+6824713260847000,1,692000,6824713261539000,786,"S",111,494,"SDM_EventThread",685
 6824713260895000,0,931000,6824713261826000,0,"R",120,"[NULL]","swapper/5",0
 6824713261084000,3,994000,6824713262078000,0,"R",120,"[NULL]","swapper/5",0
-6824713261433000,7,52000,6824713261485000,5,"S",120,7,"rcu_sched",8
-6824713261436000,2,254000,6824713261690000,40,"S",97,35,"DispSync",676
-6824713261485000,7,50000,6824713261535000,28,"S",120,26,"rcuos/6",61
+6824713261433000,7,52000,6824713261485000,6,"S",120,6,"rcu_sched",8
+6824713261436000,2,254000,6824713261690000,771,"S",97,493,"DispSync",676
+6824713261485000,7,50000,6824713261535000,53,"S",120,53,"rcuos/6",61
 6824713261535000,7,2709000,6824713264244000,0,"R",120,"[NULL]","swapper/5",0
-6824713261539000,1,1177000,6824713262716000,87,"R+",120,71,"ps",20464
+6824713261539000,1,1177000,6824713262716000,2729,"R+",120,758,"ps",20464
 6824713261690000,2,748000,6824713262438000,0,"R",120,"[NULL]","swapper/5",0
-6824713261826000,0,412000,6824713262238000,38,"S",120,35,"HwBinder:640_1",721
-6824713262078000,3,319000,6824713262397000,41,"S",97,35,"app",678
+6824713261826000,0,412000,6824713262238000,777,"S",120,493,"HwBinder:640_1",721
+6824713262078000,3,319000,6824713262397000,773,"S",97,493,"app",678
 6824713262238000,0,529000,6824713262767000,0,"R",120,"[NULL]","swapper/5",0
 6824713262397000,3,11856000,6824713274253000,0,"R",120,"[NULL]","swapper/5",0
-6824713262438000,2,46000,6824713262484000,40,"S",97,35,"DispSync",676
+6824713262438000,2,46000,6824713262484000,771,"S",97,493,"DispSync",676
 6824713262484000,2,2645000,6824713265129000,0,"R",120,"[NULL]","swapper/5",0
-6824713262716000,1,92000,6824713262808000,14,"S",120,10,"rcuos/0",11
-6824713262767000,0,2217000,6824713264984000,42,"S",120,36,"ndroid.systemui",1664
-6824713262808000,1,549000,6824713263357000,87,"R+",120,71,"ps",20464
-6824713263357000,1,1008000,6824713264365000,16,"S",120,14,"kworker/u16:7",19422
-6824713264244000,7,55000,6824713264299000,5,"S",120,7,"rcu_sched",8
+6824713262716000,1,92000,6824713262808000,9,"S",120,9,"rcuos/0",11
+6824713262767000,0,2217000,6824713264984000,644,"S",120,644,"ndroid.systemui",1664
+6824713262808000,1,549000,6824713263357000,2729,"R+",120,758,"ps",20464
+6824713263357000,1,1008000,6824713264365000,702,"S",120,702,"kworker/u16:7",19422
+6824713264244000,7,55000,6824713264299000,6,"S",120,6,"rcu_sched",8
 6824713264299000,7,7113000,6824713271412000,0,"R",120,"[NULL]","swapper/5",0
-6824713264365000,1,230000,6824713264595000,87,"R+",120,71,"ps",20464
-6824713264595000,1,23000,6824713264618000,76,"S",120,62,"hwrng",215
-6824713264618000,1,6871000,6824713271489000,87,"R+",120,71,"ps",20464
+6824713264365000,1,230000,6824713264595000,2729,"R+",120,758,"ps",20464
+6824713264595000,1,23000,6824713264618000,145,"S",120,145,"hwrng",215
+6824713264618000,1,6871000,6824713271489000,2729,"R+",120,758,"ps",20464
 6824713264984000,0,719000,6824713265703000,0,"R",120,"[NULL]","swapper/5",0
-6824713265129000,2,305000,6824713265434000,43,"S",120,35,"Binder:640_2",675
-6824713265434000,2,29000,6824713265463000,76,"S",120,62,"hwrng",215
+6824713265129000,2,305000,6824713265434000,770,"S",120,493,"Binder:640_2",675
+6824713265434000,2,29000,6824713265463000,145,"S",120,145,"hwrng",215
 6824713265463000,2,364000,6824713265827000,0,"R",120,"[NULL]","swapper/5",0
-6824713265703000,0,185000,6824713265888000,41,"S",97,35,"app",678
-6824713265827000,2,52000,6824713265879000,40,"S",97,35,"DispSync",676
+6824713265703000,0,185000,6824713265888000,773,"S",97,493,"app",678
+6824713265827000,2,52000,6824713265879000,771,"S",97,493,"DispSync",676
 6824713265879000,2,818000,6824713266697000,0,"R",120,"[NULL]","swapper/5",0
 6824713265888000,0,23236000,6824713289124000,0,"R",120,"[NULL]","swapper/5",0
-6824713266697000,2,86000,6824713266783000,6,"R+",120,4,"rcu_preempt",7
-6824713266783000,2,33000,6824713266816000,8,"S",120,8,"rcuop/6",60
-6824713266816000,2,36000,6824713266852000,6,"S",120,4,"rcu_preempt",7
+6824713266697000,2,86000,6824713266783000,5,"R+",120,5,"rcu_preempt",7
+6824713266783000,2,33000,6824713266816000,52,"S",120,52,"rcuop/6",60
+6824713266816000,2,36000,6824713266852000,5,"S",120,5,"rcu_preempt",7
 6824713266852000,2,6898000,6824713273750000,0,"R",120,"[NULL]","swapper/5",0
-6824713271412000,7,52000,6824713271464000,5,"S",120,7,"rcu_sched",8
+6824713271412000,7,52000,6824713271464000,6,"S",120,6,"rcu_sched",8
 6824713271464000,7,1149000,6824713272613000,0,"R",120,"[NULL]","swapper/5",0
-6824713271489000,1,125000,6824713271614000,14,"S",120,10,"rcuos/0",11
-6824713271614000,1,43000,6824713271657000,15,"S",120,13,"rcuos/1",21
-6824713271657000,1,2660000,6824713274317000,87,"R+",120,71,"ps",20464
-6824713272613000,7,50000,6824713272663000,5,"S",120,7,"rcu_sched",8
+6824713271489000,1,125000,6824713271614000,9,"S",120,9,"rcuos/0",11
+6824713271614000,1,43000,6824713271657000,18,"S",120,18,"rcuos/1",21
+6824713271657000,1,2660000,6824713274317000,2729,"R+",120,758,"ps",20464
+6824713272613000,7,50000,6824713272663000,6,"S",120,6,"rcu_sched",8
 6824713272663000,7,4324000,6824713276987000,0,"R",120,"[NULL]","swapper/5",0
-6824713273750000,2,109000,6824713273859000,6,"R+",120,4,"rcu_preempt",7
-6824713273859000,2,152000,6824713274011000,8,"S",120,8,"rcuop/6",60
-6824713274011000,2,24000,6824713274035000,23,"S",120,21,"rcuop/2",28
-6824713274035000,2,26000,6824713274061000,6,"S",120,4,"rcu_preempt",7
+6824713273750000,2,109000,6824713273859000,5,"R+",120,5,"rcu_preempt",7
+6824713273859000,2,152000,6824713274011000,52,"S",120,52,"rcuop/6",60
+6824713274011000,2,24000,6824713274035000,24,"S",120,24,"rcuop/2",28
+6824713274035000,2,26000,6824713274061000,5,"S",120,5,"rcu_preempt",7
 6824713274061000,2,6571000,6824713280632000,0,"R",120,"[NULL]","swapper/5",0
-6824713274253000,3,160000,6824713274413000,11,"S",120,9,"rcuop/0",10
-6824713274317000,1,223000,6824713274540000,13,"S",120,12,"rcuop/1",20
+6824713274253000,3,160000,6824713274413000,8,"S",120,8,"rcuop/0",10
+6824713274317000,1,223000,6824713274540000,17,"S",120,17,"rcuop/1",20
 6824713274413000,3,13234000,6824713287647000,0,"R",120,"[NULL]","swapper/5",0
-6824713274540000,1,2103000,6824713276643000,87,"R+",120,71,"ps",20464
-6824713276643000,1,103000,6824713276746000,16,"S",120,14,"kworker/u16:7",19422
-6824713276746000,1,294000,6824713277040000,87,"R+",120,71,"ps",20464
-6824713276987000,7,34000,6824713277021000,5,"S",120,7,"rcu_sched",8
+6824713274540000,1,2103000,6824713276643000,2729,"R+",120,758,"ps",20464
+6824713276643000,1,103000,6824713276746000,702,"S",120,702,"kworker/u16:7",19422
+6824713276746000,1,294000,6824713277040000,2729,"R+",120,758,"ps",20464
+6824713276987000,7,34000,6824713277021000,6,"S",120,6,"rcu_sched",8
 6824713277021000,7,53046000,6824713330067000,0,"R",120,"[NULL]","swapper/5",0
-6824713277040000,1,41000,6824713277081000,14,"S",120,10,"rcuos/0",11
-6824713277081000,1,23000,6824713277104000,15,"S",120,13,"rcuos/1",21
-6824713277104000,1,2914000,6824713280018000,87,"R+",120,71,"ps",20464
-6824713280018000,1,235000,6824713280253000,26,"S",49,23,"sugov:0",605
-6824713280253000,1,7441000,6824713287694000,87,"R",120,71,"ps",20464
-6824713280632000,2,120000,6824713280752000,26,"S",49,23,"sugov:0",605
-6824713280752000,2,55000,6824713280807000,6,"R+",120,4,"rcu_preempt",7
-6824713280807000,2,27000,6824713280834000,23,"S",120,21,"rcuop/2",28
-6824713280834000,2,20000,6824713280854000,6,"S",120,4,"rcu_preempt",7
+6824713277040000,1,41000,6824713277081000,9,"S",120,9,"rcuos/0",11
+6824713277081000,1,23000,6824713277104000,18,"S",120,18,"rcuos/1",21
+6824713277104000,1,2914000,6824713280018000,2729,"R+",120,758,"ps",20464
+6824713280018000,1,235000,6824713280253000,482,"S",49,482,"sugov:0",605
+6824713280253000,1,7441000,6824713287694000,2729,"R",120,758,"ps",20464
+6824713280632000,2,120000,6824713280752000,482,"S",49,482,"sugov:0",605
+6824713280752000,2,55000,6824713280807000,5,"R+",120,5,"rcu_preempt",7
+6824713280807000,2,27000,6824713280834000,24,"S",120,24,"rcuop/2",28
+6824713280834000,2,20000,6824713280854000,5,"S",120,5,"rcu_preempt",7
 6824713280854000,2,6307000,6824713287161000,0,"R",120,"[NULL]","swapper/5",0
-6824713287161000,2,80000,6824713287241000,6,"S",120,4,"rcu_preempt",7
+6824713287161000,2,80000,6824713287241000,5,"S",120,5,"rcu_preempt",7
 6824713287241000,2,805000,6824713288046000,0,"R",120,"[NULL]","swapper/5",0
-6824713287647000,3,85000,6824713287732000,11,"S",120,9,"rcuop/0",10
-6824713287694000,1,49000,6824713287743000,13,"S",120,12,"rcuop/1",20
+6824713287647000,3,85000,6824713287732000,8,"S",120,8,"rcuop/0",10
+6824713287694000,1,49000,6824713287743000,17,"S",120,17,"rcuop/1",20
 6824713287732000,3,6253000,6824713293985000,0,"R",120,"[NULL]","swapper/5",0
-6824713287743000,1,6273000,6824713294016000,87,"R+",120,71,"ps",20464
-6824713288046000,2,19000,6824713288065000,6,"S",120,4,"rcu_preempt",7
+6824713287743000,1,6273000,6824713294016000,2729,"R+",120,758,"ps",20464
+6824713288046000,2,19000,6824713288065000,5,"S",120,5,"rcu_preempt",7
 6824713288065000,2,5544000,6824713293609000,0,"R",120,"[NULL]","swapper/5",0
-6824713289124000,0,238000,6824713289362000,60,"D",100,48,"thermal-engine",2490
+6824713289124000,0,238000,6824713289362000,1694,"D",100,660,"thermal-engine",2490
 6824713289362000,0,803000,6824713290165000,0,"R",120,"[NULL]","swapper/5",0
-6824713290165000,0,67000,6824713290232000,35,"S",120,33,"kworker/0:5",20371
-6824713290232000,0,256000,6824713290488000,60,"S",100,48,"thermal-engine",2490
+6824713290165000,0,67000,6824713290232000,743,"S",120,743,"kworker/0:5",20371
+6824713290232000,0,256000,6824713290488000,1694,"S",100,660,"thermal-engine",2490
 6824713290488000,0,3702000,6824713294190000,0,"R",120,"[NULL]","swapper/5",0
-6824713293609000,2,45000,6824713293654000,6,"S",120,4,"rcu_preempt",7
+6824713293609000,2,45000,6824713293654000,5,"S",120,5,"rcu_preempt",7
 6824713293654000,2,1390000,6824713295044000,0,"R",120,"[NULL]","swapper/5",0
-6824713293985000,3,43000,6824713294028000,11,"S",120,9,"rcuop/0",10
-6824713294016000,1,29000,6824713294045000,13,"S",120,12,"rcuop/1",20
+6824713293985000,3,43000,6824713294028000,8,"S",120,8,"rcuop/0",10
+6824713294016000,1,29000,6824713294045000,17,"S",120,17,"rcuop/1",20
 6824713294028000,3,1525000,6824713295553000,0,"R",120,"[NULL]","swapper/5",0
-6824713294045000,1,182000,6824713294227000,87,"R",120,71,"ps",20464
-6824713294190000,0,70000,6824713294260000,35,"S",120,33,"kworker/0:5",20371
-6824713294227000,1,650000,6824713294877000,36,"S",111,34,"SDM_EventThread",685
+6824713294045000,1,182000,6824713294227000,2729,"R",120,758,"ps",20464
+6824713294190000,0,70000,6824713294260000,743,"S",120,743,"kworker/0:5",20371
+6824713294227000,1,650000,6824713294877000,786,"S",111,494,"SDM_EventThread",685
 6824713294260000,0,501000,6824713294761000,0,"R",120,"[NULL]","swapper/5",0
-6824713294761000,0,350000,6824713295111000,38,"S",120,35,"HwBinder:640_1",721
-6824713294877000,1,8453000,6824713303330000,87,"R+",120,71,"ps",20464
-6824713295044000,2,186000,6824713295230000,40,"S",97,35,"DispSync",676
+6824713294761000,0,350000,6824713295111000,777,"S",120,493,"HwBinder:640_1",721
+6824713294877000,1,8453000,6824713303330000,2729,"R+",120,758,"ps",20464
+6824713295044000,2,186000,6824713295230000,771,"S",97,493,"DispSync",676
 6824713295111000,0,16000,6824713295127000,0,"R",120,"[NULL]","swapper/5",0
-6824713295127000,0,113000,6824713295240000,38,"S",120,35,"HwBinder:640_1",721
+6824713295127000,0,113000,6824713295240000,777,"S",120,493,"HwBinder:640_1",721
 6824713295230000,2,1013000,6824713296243000,0,"R",120,"[NULL]","swapper/5",0
 6824713295240000,0,922000,6824713296162000,0,"R",120,"[NULL]","swapper/5",0
-6824713295553000,3,326000,6824713295879000,41,"S",97,35,"app",678
+6824713295553000,3,326000,6824713295879000,773,"S",97,493,"app",678
 6824713295879000,3,4297000,6824713300176000,0,"R",120,"[NULL]","swapper/5",0
-6824713296162000,0,1958000,6824713298120000,42,"S",120,36,"ndroid.systemui",1664
-6824713296243000,2,31000,6824713296274000,40,"S",97,35,"DispSync",676
+6824713296162000,0,1958000,6824713298120000,644,"S",120,644,"ndroid.systemui",1664
+6824713296243000,2,31000,6824713296274000,771,"S",97,493,"DispSync",676
 6824713296274000,2,1983000,6824713298257000,0,"R",120,"[NULL]","swapper/5",0
 6824713298120000,0,672000,6824713298792000,0,"R",120,"[NULL]","swapper/5",0
-6824713298257000,2,260000,6824713298517000,43,"S",120,35,"Binder:640_2",675
+6824713298257000,2,260000,6824713298517000,770,"S",120,493,"Binder:640_2",675
 6824713298517000,2,708000,6824713299225000,0,"R",120,"[NULL]","swapper/5",0
-6824713298792000,0,153000,6824713298945000,41,"S",97,35,"app",678
+6824713298792000,0,153000,6824713298945000,773,"S",97,493,"app",678
 6824713298945000,0,25615000,6824713324560000,0,"R",120,"[NULL]","swapper/5",0
-6824713299225000,2,37000,6824713299262000,40,"S",97,35,"DispSync",676
+6824713299225000,2,37000,6824713299262000,771,"S",97,493,"DispSync",676
 6824713299262000,2,1065000,6824713300327000,0,"R",120,"[NULL]","swapper/5",0
-6824713300176000,3,48000,6824713300224000,11,"S",120,9,"rcuop/0",10
+6824713300176000,3,48000,6824713300224000,8,"S",120,8,"rcuop/0",10
 6824713300224000,3,7418000,6824713307642000,0,"R",120,"[NULL]","swapper/5",0
-6824713300327000,2,21000,6824713300348000,76,"S",120,62,"hwrng",215
-6824713300348000,2,36000,6824713300384000,6,"S",120,4,"rcu_preempt",7
+6824713300327000,2,21000,6824713300348000,145,"S",120,145,"hwrng",215
+6824713300348000,2,36000,6824713300384000,5,"S",120,5,"rcu_preempt",7
 6824713300384000,2,6852000,6824713307236000,0,"R",120,"[NULL]","swapper/5",0
-6824713303330000,1,176000,6824713303506000,26,"S",49,23,"sugov:0",605
-6824713303506000,1,3115000,6824713306621000,87,"R+",120,71,"ps",20464
-6824713306621000,1,71000,6824713306692000,26,"S",49,23,"sugov:0",605
-6824713306692000,1,994000,6824713307686000,87,"R+",120,71,"ps",20464
-6824713307236000,2,69000,6824713307305000,6,"S",120,4,"rcu_preempt",7
+6824713303330000,1,176000,6824713303506000,482,"S",49,482,"sugov:0",605
+6824713303506000,1,3115000,6824713306621000,2729,"R+",120,758,"ps",20464
+6824713306621000,1,71000,6824713306692000,482,"S",49,482,"sugov:0",605
+6824713306692000,1,994000,6824713307686000,2729,"R+",120,758,"ps",20464
+6824713307236000,2,69000,6824713307305000,5,"S",120,5,"rcu_preempt",7
 6824713307305000,2,404000,6824713307709000,0,"R",120,"[NULL]","swapper/5",0
-6824713307642000,3,72000,6824713307714000,11,"S",120,9,"rcuop/0",10
-6824713307686000,1,32000,6824713307718000,13,"S",120,12,"rcuop/1",20
-6824713307709000,2,17000,6824713307726000,6,"S",120,4,"rcu_preempt",7
+6824713307642000,3,72000,6824713307714000,8,"S",120,8,"rcuop/0",10
+6824713307686000,1,32000,6824713307718000,17,"S",120,17,"rcuop/1",20
+6824713307709000,2,17000,6824713307726000,5,"S",120,5,"rcu_preempt",7
 6824713307714000,3,9638000,6824713317352000,0,"R",120,"[NULL]","swapper/5",0
-6824713307718000,1,9664000,6824713317382000,87,"R+",120,71,"ps",20464
+6824713307718000,1,9664000,6824713317382000,2729,"R+",120,758,"ps",20464
 6824713307726000,2,5944000,6824713313670000,0,"R",120,"[NULL]","swapper/5",0
-6824713313670000,2,31000,6824713313701000,6,"S",120,4,"rcu_preempt",7
+6824713313670000,2,31000,6824713313701000,5,"S",120,5,"rcu_preempt",7
 6824713313701000,2,3234000,6824713316935000,0,"R",120,"[NULL]","swapper/5",0
-6824713316935000,2,46000,6824713316981000,6,"S",120,4,"rcu_preempt",7
+6824713316935000,2,46000,6824713316981000,5,"S",120,5,"rcu_preempt",7
 6824713316981000,2,773000,6824713317754000,0,"R",120,"[NULL]","swapper/5",0
-6824713317352000,3,56000,6824713317408000,11,"S",120,9,"rcuop/0",10
-6824713317382000,1,116000,6824713317498000,13,"S",120,12,"rcuop/1",20
+6824713317352000,3,56000,6824713317408000,8,"S",120,8,"rcuop/0",10
+6824713317382000,1,116000,6824713317498000,17,"S",120,17,"rcuop/1",20
 6824713317408000,3,6640000,6824713324048000,0,"R",120,"[NULL]","swapper/5",0
-6824713317498000,1,5825000,6824713323323000,87,"R+",120,71,"ps",20464
-6824713317754000,2,15000,6824713317769000,6,"S",120,4,"rcu_preempt",7
+6824713317498000,1,5825000,6824713323323000,2729,"R+",120,758,"ps",20464
+6824713317754000,2,15000,6824713317769000,5,"S",120,5,"rcu_preempt",7
 6824713317769000,2,5877000,6824713323646000,0,"R",120,"[NULL]","swapper/5",0
-6824713323323000,1,140000,6824713323463000,26,"S",49,23,"sugov:0",605
-6824713323463000,1,4344000,6824713327807000,87,"R+",120,71,"ps",20464
-6824713323646000,2,54000,6824713323700000,6,"S",120,4,"rcu_preempt",7
+6824713323323000,1,140000,6824713323463000,482,"S",49,482,"sugov:0",605
+6824713323463000,1,4344000,6824713327807000,2729,"R+",120,758,"ps",20464
+6824713323646000,2,54000,6824713323700000,5,"S",120,5,"rcu_preempt",7
 6824713323700000,2,893000,6824713324593000,0,"R",120,"[NULL]","swapper/5",0
-6824713324048000,3,59000,6824713324107000,11,"S",120,9,"rcuop/0",10
+6824713324048000,3,59000,6824713324107000,8,"S",120,8,"rcuop/0",10
 6824713324107000,3,3572000,6824713327679000,0,"R",120,"[NULL]","swapper/5",0
-6824713324560000,0,209000,6824713324769000,13,"S",120,12,"rcuop/1",20
-6824713324593000,2,17000,6824713324610000,6,"S",120,4,"rcu_preempt",7
+6824713324560000,0,209000,6824713324769000,17,"S",120,17,"rcuop/1",20
+6824713324593000,2,17000,6824713324610000,5,"S",120,5,"rcu_preempt",7
 6824713324610000,2,2330000,6824713326940000,0,"R",120,"[NULL]","swapper/5",0
 6824713324769000,0,2944000,6824713327713000,0,"R",120,"[NULL]","swapper/5",0
-6824713326940000,2,357000,6824713327297000,16,"D",120,14,"kworker/u16:7",19422
+6824713326940000,2,357000,6824713327297000,702,"D",120,702,"kworker/u16:7",19422
 6824713327297000,2,154000,6824713327451000,0,"R",120,"[NULL]","swapper/5",0
-6824713327451000,2,31000,6824713327482000,16,"S",120,14,"kworker/u16:7",19422
+6824713327451000,2,31000,6824713327482000,702,"S",120,702,"kworker/u16:7",19422
 6824713327482000,2,276000,6824713327758000,0,"R",120,"[NULL]","swapper/5",0
-6824713327679000,3,43000,6824713327722000,32,"S",120,30,"smem_native_rpm",87
-6824713327713000,0,92000,6824713327805000,35,"S",120,33,"kworker/0:5",20371
+6824713327679000,3,43000,6824713327722000,77,"S",120,77,"smem_native_rpm",87
+6824713327713000,0,92000,6824713327805000,743,"S",120,743,"kworker/0:5",20371
 6824713327722000,3,1218000,6824713328940000,0,"R",120,"[NULL]","swapper/5",0
-6824713327758000,2,602000,6824713328360000,36,"S",111,34,"SDM_EventThread",685
+6824713327758000,2,602000,6824713328360000,786,"S",111,494,"SDM_EventThread",685
 6824713327805000,0,570000,6824713328375000,0,"R",120,"[NULL]","swapper/5",0
-6824713327807000,1,48000,6824713327855000,88,"S",0,72,"migration/1",16
+6824713327807000,1,48000,6824713327855000,14,"S",0,14,"migration/1",16
 6824713327855000,1,1280000,6824713329135000,0,"R",120,"[NULL]","swapper/5",0
 6824713328360000,2,884000,6824713329244000,0,"R",120,"[NULL]","swapper/5",0
-6824713328375000,0,1267000,6824713329642000,87,"R",120,71,"ps",20464
-6824713328940000,3,397000,6824713329337000,38,"S",120,35,"HwBinder:640_1",721
-6824713329043000,6,147000,6824713329190000,22,"S",49,20,"sugov:4",606
-6824713329135000,1,153000,6824713329288000,40,"S",97,35,"DispSync",676
-6824713329190000,6,433000,6824713329623000,82,"S",120,5,"shell",20461
-6824713329244000,2,244000,6824713329488000,41,"S",97,35,"app",678
+6824713328375000,0,1267000,6824713329642000,2729,"R",120,758,"ps",20464
+6824713328940000,3,397000,6824713329337000,777,"S",120,493,"HwBinder:640_1",721
+6824713329043000,6,147000,6824713329190000,483,"S",49,483,"sugov:4",606
+6824713329135000,1,153000,6824713329288000,771,"S",97,493,"DispSync",676
+6824713329190000,6,433000,6824713329623000,2728,"S",120,739,"shell",20461
+6824713329244000,2,244000,6824713329488000,773,"S",97,493,"app",678
 6824713329288000,1,523000,6824713329811000,0,"R",120,"[NULL]","swapper/5",0
 6824713329337000,3,722000,6824713330059000,0,"R",120,"[NULL]","swapper/5",0
 6824713329488000,2,470000,6824713329958000,0,"R",120,"[NULL]","swapper/5",0
 6824713329623000,6,113000,6824713329736000,0,"R",120,"[NULL]","swapper/5",0
-6824713329642000,0,49000,6824713329691000,86,"S",0,70,"migration/0",13
+6824713329642000,0,49000,6824713329691000,11,"S",0,11,"migration/0",13
 6824713329691000,0,2040000,6824713331731000,0,"R",120,"[NULL]","swapper/5",0
-6824713329736000,6,3595000,6824713333331000,87,"R+",120,71,"ps",20464
-6824713329811000,1,17000,6824713329828000,40,"S",97,35,"DispSync",676
+6824713329736000,6,3595000,6824713333331000,2729,"R+",120,758,"ps",20464
+6824713329811000,1,17000,6824713329828000,771,"S",97,493,"DispSync",676
 6824713329828000,1,2390000,6824713332218000,0,"R",120,"[NULL]","swapper/5",0
-6824713329958000,2,20000,6824713329978000,8,"S",120,8,"rcuop/6",60
-6824713329978000,2,29000,6824713330007000,6,"S",120,4,"rcu_preempt",7
+6824713329958000,2,20000,6824713329978000,52,"S",120,52,"rcuop/6",60
+6824713329978000,2,29000,6824713330007000,5,"S",120,5,"rcu_preempt",7
 6824713330007000,2,1145000,6824713331152000,0,"R",120,"[NULL]","swapper/5",0
-6824713330059000,3,1520000,6824713331579000,42,"S",120,36,"ndroid.systemui",1664
-6824713330067000,7,915000,6824713330982000,7,"S",120,5,"adbd",20305
+6824713330059000,3,1520000,6824713331579000,644,"S",120,644,"ndroid.systemui",1664
+6824713330067000,7,915000,6824713330982000,739,"S",120,739,"adbd",20305
 6824713330982000,7,71506000,6824713402488000,0,"R",120,"[NULL]","swapper/5",0
-6824713331152000,2,172000,6824713331324000,79,"S",100,64,"kworker/u17:2",14944
-6824713331324000,2,62000,6824713331386000,31,"S",120,29,"kworker/2:0",18823
-6824713331386000,2,66000,6824713331452000,2,"S",100,2,"kworker/u17:1",1134
-6824713331452000,2,4000,6824713331456000,79,"S",100,64,"kworker/u17:2",14944
-6824713331456000,2,12000,6824713331468000,31,"S",120,29,"kworker/2:0",18823
-6824713331468000,2,57000,6824713331525000,4,"R",120,5,"UsbFfs-worker",20308
-6824713331525000,2,14000,6824713331539000,79,"S",100,64,"kworker/u17:2",14944
-6824713331539000,2,119000,6824713331658000,4,"S",120,5,"UsbFfs-worker",20308
+6824713331152000,2,172000,6824713331324000,670,"S",100,670,"kworker/u17:2",14944
+6824713331324000,2,62000,6824713331386000,694,"S",120,694,"kworker/2:0",18823
+6824713331386000,2,66000,6824713331452000,630,"S",100,630,"kworker/u17:1",1134
+6824713331452000,2,4000,6824713331456000,670,"S",100,670,"kworker/u17:2",14944
+6824713331456000,2,12000,6824713331468000,694,"S",120,694,"kworker/2:0",18823
+6824713331468000,2,57000,6824713331525000,2487,"R",120,739,"UsbFfs-worker",20308
+6824713331525000,2,14000,6824713331539000,670,"S",100,670,"kworker/u17:2",14944
+6824713331539000,2,119000,6824713331658000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713331579000,3,5762000,6824713337341000,0,"R",120,"[NULL]","swapper/5",0
 6824713331658000,2,60000,6824713331718000,0,"R",120,"[NULL]","swapper/5",0
-6824713331718000,2,33000,6824713331751000,79,"S",100,64,"kworker/u17:2",14944
-6824713331731000,0,215000,6824713331946000,43,"S",120,35,"Binder:640_2",675
+6824713331718000,2,33000,6824713331751000,670,"S",100,670,"kworker/u17:2",14944
+6824713331731000,0,215000,6824713331946000,770,"S",120,493,"Binder:640_2",675
 6824713331751000,2,33000,6824713331784000,0,"R",120,"[NULL]","swapper/5",0
-6824713331784000,2,39000,6824713331823000,79,"S",100,64,"kworker/u17:2",14944
-6824713331823000,2,43000,6824713331866000,31,"S",120,29,"kworker/2:0",18823
-6824713331866000,2,172000,6824713332038000,4,"S",120,5,"UsbFfs-worker",20308
+6824713331784000,2,39000,6824713331823000,670,"S",100,670,"kworker/u17:2",14944
+6824713331823000,2,43000,6824713331866000,694,"S",120,694,"kworker/2:0",18823
+6824713331866000,2,172000,6824713332038000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713331946000,0,667000,6824713332613000,0,"R",120,"[NULL]","swapper/5",0
-6824713332038000,2,397000,6824713332435000,7,"S",120,5,"adbd",20305
-6824713332218000,1,104000,6824713332322000,41,"S",97,35,"app",678
+6824713332038000,2,397000,6824713332435000,739,"S",120,739,"adbd",20305
+6824713332218000,1,104000,6824713332322000,773,"S",97,493,"app",678
 6824713332322000,1,30785000,6824713363107000,0,"R",120,"[NULL]","swapper/5",0
 6824713332435000,2,4516000,6824713336951000,0,"R",120,"[NULL]","swapper/5",0
-6824713332613000,0,24000,6824713332637000,40,"S",97,35,"DispSync",676
+6824713332613000,0,24000,6824713332637000,771,"S",97,493,"DispSync",676
 6824713332637000,0,5094000,6824713337731000,0,"R",120,"[NULL]","swapper/5",0
-6824713333331000,6,103000,6824713333434000,22,"S",49,20,"sugov:4",606
-6824713333434000,6,9802000,6824713343236000,87,"R+",120,71,"ps",20464
-6824713336951000,2,72000,6824713337023000,6,"S",120,4,"rcu_preempt",7
+6824713333331000,6,103000,6824713333434000,483,"S",49,483,"sugov:4",606
+6824713333434000,6,9802000,6824713343236000,2729,"R+",120,758,"ps",20464
+6824713336951000,2,72000,6824713337023000,5,"S",120,5,"rcu_preempt",7
 6824713337023000,2,6609000,6824713343632000,0,"R",120,"[NULL]","swapper/5",0
-6824713337341000,3,53000,6824713337394000,11,"S",120,9,"rcuop/0",10
+6824713337341000,3,53000,6824713337394000,8,"S",120,8,"rcuop/0",10
 6824713337394000,3,14884000,6824713352278000,0,"R",120,"[NULL]","swapper/5",0
-6824713337731000,0,167000,6824713337898000,13,"S",120,12,"rcuop/1",20
+6824713337731000,0,167000,6824713337898000,17,"S",120,17,"rcuop/1",20
 6824713337898000,0,14895000,6824713352793000,0,"R",120,"[NULL]","swapper/5",0
-6824713343236000,6,17000,6824713343253000,22,"S",49,20,"sugov:4",606
-6824713343253000,6,797000,6824713344050000,87,"R+",120,71,"ps",20464
-6824713343632000,2,147000,6824713343779000,26,"S",49,23,"sugov:0",605
-6824713343779000,2,140000,6824713343919000,6,"R+",120,4,"rcu_preempt",7
-6824713343919000,2,29000,6824713343948000,8,"S",120,8,"rcuop/6",60
-6824713343948000,2,28000,6824713343976000,6,"S",120,4,"rcu_preempt",7
+6824713343236000,6,17000,6824713343253000,483,"S",49,483,"sugov:4",606
+6824713343253000,6,797000,6824713344050000,2729,"R+",120,758,"ps",20464
+6824713343632000,2,147000,6824713343779000,482,"S",49,482,"sugov:0",605
+6824713343779000,2,140000,6824713343919000,5,"R+",120,5,"rcu_preempt",7
+6824713343919000,2,29000,6824713343948000,52,"S",120,52,"rcuop/6",60
+6824713343948000,2,28000,6824713343976000,5,"S",120,5,"rcu_preempt",7
 6824713343976000,2,7683000,6824713351659000,0,"R",120,"[NULL]","swapper/5",0
-6824713344050000,6,47000,6824713344097000,82,"R+",120,5,"shell",20461
-6824713344097000,6,32000,6824713344129000,22,"S",49,20,"sugov:4",606
-6824713344129000,6,10000,6824713344139000,82,"S",120,5,"shell",20461
-6824713344139000,6,5756000,6824713349895000,87,"R+",120,71,"ps",20464
-6824713349895000,6,389000,6824713350284000,7,"S",120,5,"adbd",20305
-6824713350284000,6,1235000,6824713351519000,87,"R+",120,71,"ps",20464
-6824713351519000,6,22000,6824713351541000,22,"S",49,20,"sugov:4",606
-6824713351541000,6,108000,6824713351649000,79,"S",100,64,"kworker/u17:2",14944
-6824713351649000,6,56000,6824713351705000,3,"S",120,3,"kworker/6:1",14833
-6824713351659000,2,259000,6824713351918000,6,"R+",120,4,"rcu_preempt",7
-6824713351705000,6,212000,6824713351917000,87,"R+",120,71,"ps",20464
-6824713351917000,6,39000,6824713351956000,79,"S",100,64,"kworker/u17:2",14944
-6824713351918000,2,591000,6824713352509000,8,"S",120,8,"rcuop/6",60
-6824713351956000,6,11000,6824713351967000,3,"S",120,3,"kworker/6:1",14833
-6824713351967000,6,121000,6824713352088000,87,"R",120,71,"ps",20464
-6824713352088000,6,38000,6824713352126000,79,"S",100,64,"kworker/u17:2",14944
-6824713352126000,6,58000,6824713352184000,87,"R+",120,71,"ps",20464
-6824713352184000,6,60000,6824713352244000,79,"S",100,64,"kworker/u17:2",14944
-6824713352244000,6,24000,6824713352268000,3,"S",120,3,"kworker/6:1",14833
-6824713352268000,6,6413000,6824713358681000,87,"R+",120,71,"ps",20464
-6824713352278000,3,219000,6824713352497000,11,"S",120,9,"rcuop/0",10
+6824713344050000,6,47000,6824713344097000,2728,"R+",120,739,"shell",20461
+6824713344097000,6,32000,6824713344129000,483,"S",49,483,"sugov:4",606
+6824713344129000,6,10000,6824713344139000,2728,"S",120,739,"shell",20461
+6824713344139000,6,5756000,6824713349895000,2729,"R+",120,758,"ps",20464
+6824713349895000,6,389000,6824713350284000,739,"S",120,739,"adbd",20305
+6824713350284000,6,1235000,6824713351519000,2729,"R+",120,758,"ps",20464
+6824713351519000,6,22000,6824713351541000,483,"S",49,483,"sugov:4",606
+6824713351541000,6,108000,6824713351649000,670,"S",100,670,"kworker/u17:2",14944
+6824713351649000,6,56000,6824713351705000,669,"S",120,669,"kworker/6:1",14833
+6824713351659000,2,259000,6824713351918000,5,"R+",120,5,"rcu_preempt",7
+6824713351705000,6,212000,6824713351917000,2729,"R+",120,758,"ps",20464
+6824713351917000,6,39000,6824713351956000,670,"S",100,670,"kworker/u17:2",14944
+6824713351918000,2,591000,6824713352509000,52,"S",120,52,"rcuop/6",60
+6824713351956000,6,11000,6824713351967000,669,"S",120,669,"kworker/6:1",14833
+6824713351967000,6,121000,6824713352088000,2729,"R",120,758,"ps",20464
+6824713352088000,6,38000,6824713352126000,670,"S",100,670,"kworker/u17:2",14944
+6824713352126000,6,58000,6824713352184000,2729,"R+",120,758,"ps",20464
+6824713352184000,6,60000,6824713352244000,670,"S",100,670,"kworker/u17:2",14944
+6824713352244000,6,24000,6824713352268000,669,"S",120,669,"kworker/6:1",14833
+6824713352268000,6,6413000,6824713358681000,2729,"R+",120,758,"ps",20464
+6824713352278000,3,219000,6824713352497000,8,"S",120,8,"rcuop/0",10
 6824713352497000,3,11953000,6824713364450000,0,"R",120,"[NULL]","swapper/5",0
-6824713352509000,2,289000,6824713352798000,4,"R+",120,5,"UsbFfs-worker",20308
-6824713352793000,0,205000,6824713352998000,13,"S",120,12,"rcuop/1",20
-6824713352798000,2,418000,6824713353216000,7,"S",120,5,"adbd",20305
+6824713352509000,2,289000,6824713352798000,2487,"R+",120,739,"UsbFfs-worker",20308
+6824713352793000,0,205000,6824713352998000,17,"S",120,17,"rcuop/1",20
+6824713352798000,2,418000,6824713353216000,739,"S",120,739,"adbd",20305
 6824713352998000,0,8273000,6824713361271000,0,"R",120,"[NULL]","swapper/5",0
-6824713353216000,2,150000,6824713353366000,4,"R+",120,5,"UsbFfs-worker",20308
-6824713353366000,2,154000,6824713353520000,7,"S",120,5,"adbd",20305
-6824713353520000,2,163000,6824713353683000,4,"S",120,5,"UsbFfs-worker",20308
-6824713353683000,2,57000,6824713353740000,6,"S",120,4,"rcu_preempt",7
+6824713353216000,2,150000,6824713353366000,2487,"R+",120,739,"UsbFfs-worker",20308
+6824713353366000,2,154000,6824713353520000,739,"S",120,739,"adbd",20305
+6824713353520000,2,163000,6824713353683000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713353683000,2,57000,6824713353740000,5,"S",120,5,"rcu_preempt",7
 6824713353740000,2,7167000,6824713360907000,0,"R",120,"[NULL]","swapper/5",0
-6824713358681000,6,60000,6824713358741000,82,"S",120,5,"shell",20461
-6824713358741000,6,311000,6824713359052000,7,"S",120,5,"adbd",20305
-6824713359052000,6,875000,6824713359927000,87,"R+",120,71,"ps",20464
-6824713359927000,6,41000,6824713359968000,3,"S",120,3,"kworker/6:1",14833
-6824713359968000,6,3312000,6824713363280000,87,"R",120,71,"ps",20464
-6824713360907000,2,128000,6824713361035000,6,"R+",120,4,"rcu_preempt",7
-6824713361035000,2,355000,6824713361390000,8,"S",120,8,"rcuop/6",60
-6824713361271000,0,118000,6824713361389000,79,"S",100,64,"kworker/u17:2",14944
-6824713361389000,0,127000,6824713361516000,35,"R+",120,33,"kworker/0:5",20371
-6824713361390000,2,51000,6824713361441000,6,"S",120,4,"rcu_preempt",7
+6824713358681000,6,60000,6824713358741000,2728,"S",120,739,"shell",20461
+6824713358741000,6,311000,6824713359052000,739,"S",120,739,"adbd",20305
+6824713359052000,6,875000,6824713359927000,2729,"R+",120,758,"ps",20464
+6824713359927000,6,41000,6824713359968000,669,"S",120,669,"kworker/6:1",14833
+6824713359968000,6,3312000,6824713363280000,2729,"R",120,758,"ps",20464
+6824713360907000,2,128000,6824713361035000,5,"R+",120,5,"rcu_preempt",7
+6824713361035000,2,355000,6824713361390000,52,"S",120,52,"rcuop/6",60
+6824713361271000,0,118000,6824713361389000,670,"S",100,670,"kworker/u17:2",14944
+6824713361389000,0,127000,6824713361516000,743,"R+",120,743,"kworker/0:5",20371
+6824713361390000,2,51000,6824713361441000,5,"S",120,5,"rcu_preempt",7
 6824713361441000,2,95000,6824713361536000,0,"R",120,"[NULL]","swapper/5",0
-6824713361516000,0,44000,6824713361560000,79,"S",100,64,"kworker/u17:2",14944
-6824713361536000,2,952000,6824713362488000,36,"S",111,34,"SDM_EventThread",685
-6824713361560000,0,61000,6824713361621000,35,"R+",120,33,"kworker/0:5",20371
-6824713361621000,0,33000,6824713361654000,79,"S",100,64,"kworker/u17:2",14944
-6824713361654000,0,67000,6824713361721000,35,"S",120,33,"kworker/0:5",20371
+6824713361516000,0,44000,6824713361560000,670,"S",100,670,"kworker/u17:2",14944
+6824713361536000,2,952000,6824713362488000,786,"S",111,494,"SDM_EventThread",685
+6824713361560000,0,61000,6824713361621000,743,"R+",120,743,"kworker/0:5",20371
+6824713361621000,0,33000,6824713361654000,670,"S",100,670,"kworker/u17:2",14944
+6824713361654000,0,67000,6824713361721000,743,"S",120,743,"kworker/0:5",20371
 6824713361721000,0,80000,6824713361801000,0,"R",120,"[NULL]","swapper/5",0
-6824713361801000,0,57000,6824713361858000,79,"S",100,64,"kworker/u17:2",14944
+6824713361801000,0,57000,6824713361858000,670,"S",100,670,"kworker/u17:2",14944
 6824713361858000,0,46000,6824713361904000,0,"R",120,"[NULL]","swapper/5",0
-6824713361904000,0,39000,6824713361943000,79,"S",100,64,"kworker/u17:2",14944
+6824713361904000,0,39000,6824713361943000,670,"S",100,670,"kworker/u17:2",14944
 6824713361943000,0,43000,6824713361986000,0,"R",120,"[NULL]","swapper/5",0
-6824713361986000,0,70000,6824713362056000,79,"S",100,64,"kworker/u17:2",14944
-6824713362056000,0,69000,6824713362125000,35,"S",120,33,"kworker/0:5",20371
+6824713361986000,0,70000,6824713362056000,670,"S",100,670,"kworker/u17:2",14944
+6824713362056000,0,69000,6824713362125000,743,"S",120,743,"kworker/0:5",20371
 6824713362125000,0,232000,6824713362357000,0,"R",120,"[NULL]","swapper/5",0
-6824713362357000,0,537000,6824713362894000,38,"S",120,35,"HwBinder:640_1",721
-6824713362488000,2,279000,6824713362767000,4,"R+",120,5,"UsbFfs-worker",20308
-6824713362767000,2,401000,6824713363168000,7,"S",120,5,"adbd",20305
+6824713362357000,0,537000,6824713362894000,777,"S",120,493,"HwBinder:640_1",721
+6824713362488000,2,279000,6824713362767000,2487,"R+",120,739,"UsbFfs-worker",20308
+6824713362767000,2,401000,6824713363168000,739,"S",120,739,"adbd",20305
 6824713362894000,0,738000,6824713363632000,0,"R",120,"[NULL]","swapper/5",0
-6824713363107000,1,274000,6824713363381000,40,"S",97,35,"DispSync",676
-6824713363168000,2,157000,6824713363325000,4,"R+",120,5,"UsbFfs-worker",20308
-6824713363280000,6,49000,6824713363329000,22,"S",49,20,"sugov:4",606
-6824713363325000,2,157000,6824713363482000,7,"S",120,5,"adbd",20305
-6824713363329000,6,6630000,6824713369959000,87,"R+",120,71,"ps",20464
+6824713363107000,1,274000,6824713363381000,771,"S",97,493,"DispSync",676
+6824713363168000,2,157000,6824713363325000,2487,"R+",120,739,"UsbFfs-worker",20308
+6824713363280000,6,49000,6824713363329000,483,"S",49,483,"sugov:4",606
+6824713363325000,2,157000,6824713363482000,739,"S",120,739,"adbd",20305
+6824713363329000,6,6630000,6824713369959000,2729,"R+",120,758,"ps",20464
 6824713363381000,1,919000,6824713364300000,0,"R",120,"[NULL]","swapper/5",0
-6824713363482000,2,776000,6824713364258000,16,"S",120,14,"kworker/u16:7",19422
-6824713363632000,0,355000,6824713363987000,41,"S",97,35,"app",678
+6824713363482000,2,776000,6824713364258000,702,"S",120,702,"kworker/u16:7",19422
+6824713363632000,0,355000,6824713363987000,773,"S",97,493,"app",678
 6824713363987000,0,2520000,6824713366507000,0,"R",120,"[NULL]","swapper/5",0
-6824713364258000,2,164000,6824713364422000,4,"S",120,5,"UsbFfs-worker",20308
-6824713364300000,1,2117000,6824713366417000,42,"S",120,36,"ndroid.systemui",1664
+6824713364258000,2,164000,6824713364422000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713364300000,1,2117000,6824713366417000,644,"S",120,644,"ndroid.systemui",1664
 6824713364422000,2,2600000,6824713367022000,0,"R",120,"[NULL]","swapper/5",0
-6824713364450000,3,65000,6824713364515000,40,"S",97,35,"DispSync",676
+6824713364450000,3,65000,6824713364515000,771,"S",97,493,"DispSync",676
 6824713364515000,3,13500000,6824713378015000,0,"R",120,"[NULL]","swapper/5",0
 6824713366417000,1,380000,6824713366797000,0,"R",120,"[NULL]","swapper/5",0
-6824713366507000,0,360000,6824713366867000,43,"S",120,35,"Binder:640_2",675
-6824713366797000,1,143000,6824713366940000,41,"S",97,35,"app",678
+6824713366507000,0,360000,6824713366867000,770,"S",120,493,"Binder:640_2",675
+6824713366797000,1,143000,6824713366940000,773,"S",97,493,"app",678
 6824713366867000,0,5605000,6824713372472000,0,"R",120,"[NULL]","swapper/5",0
 6824713366940000,1,28138000,6824713395078000,0,"R",120,"[NULL]","swapper/5",0
-6824713367022000,2,33000,6824713367055000,40,"S",97,35,"DispSync",676
-6824713367055000,2,85000,6824713367140000,6,"R+",120,4,"rcu_preempt",7
-6824713367140000,2,409000,6824713367549000,8,"S",120,8,"rcuop/6",60
-6824713367549000,2,34000,6824713367583000,6,"S",120,4,"rcu_preempt",7
+6824713367022000,2,33000,6824713367055000,771,"S",97,493,"DispSync",676
+6824713367055000,2,85000,6824713367140000,5,"R+",120,5,"rcu_preempt",7
+6824713367140000,2,409000,6824713367549000,52,"S",120,52,"rcuop/6",60
+6824713367549000,2,34000,6824713367583000,5,"S",120,5,"rcu_preempt",7
 6824713367583000,2,5336000,6824713372919000,0,"R",120,"[NULL]","swapper/5",0
-6824713369959000,6,65000,6824713370024000,82,"R+",120,5,"shell",20461
-6824713370024000,6,36000,6824713370060000,22,"S",49,20,"sugov:4",606
-6824713370060000,6,15000,6824713370075000,82,"S",120,5,"shell",20461
-6824713370075000,6,299000,6824713370374000,7,"S",120,5,"adbd",20305
-6824713370374000,6,10921000,6824713381295000,87,"R+",120,71,"ps",20464
-6824713372472000,0,237000,6824713372709000,79,"R+",100,64,"kworker/u17:2",14944
-6824713372709000,0,188000,6824713372897000,26,"S",49,23,"sugov:0",605
-6824713372897000,0,74000,6824713372971000,79,"S",100,64,"kworker/u17:2",14944
-6824713372919000,2,89000,6824713373008000,2,"S",100,2,"kworker/u17:1",1134
-6824713372971000,0,177000,6824713373148000,35,"R+",120,33,"kworker/0:5",20371
+6824713369959000,6,65000,6824713370024000,2728,"R+",120,739,"shell",20461
+6824713370024000,6,36000,6824713370060000,483,"S",49,483,"sugov:4",606
+6824713370060000,6,15000,6824713370075000,2728,"S",120,739,"shell",20461
+6824713370075000,6,299000,6824713370374000,739,"S",120,739,"adbd",20305
+6824713370374000,6,10921000,6824713381295000,2729,"R+",120,758,"ps",20464
+6824713372472000,0,237000,6824713372709000,670,"R+",100,670,"kworker/u17:2",14944
+6824713372709000,0,188000,6824713372897000,482,"S",49,482,"sugov:0",605
+6824713372897000,0,74000,6824713372971000,670,"S",100,670,"kworker/u17:2",14944
+6824713372919000,2,89000,6824713373008000,630,"S",100,630,"kworker/u17:1",1134
+6824713372971000,0,177000,6824713373148000,743,"R+",120,743,"kworker/0:5",20371
 6824713373008000,2,144000,6824713373152000,0,"R",120,"[NULL]","swapper/5",0
-6824713373148000,0,64000,6824713373212000,79,"S",100,64,"kworker/u17:2",14944
-6824713373152000,2,734000,6824713373886000,4,"R+",120,5,"UsbFfs-worker",20308
-6824713373212000,0,142000,6824713373354000,35,"R+",120,33,"kworker/0:5",20371
-6824713373354000,0,79000,6824713373433000,79,"S",100,64,"kworker/u17:2",14944
-6824713373433000,0,170000,6824713373603000,35,"S",120,33,"kworker/0:5",20371
+6824713373148000,0,64000,6824713373212000,670,"S",100,670,"kworker/u17:2",14944
+6824713373152000,2,734000,6824713373886000,2487,"R+",120,739,"UsbFfs-worker",20308
+6824713373212000,0,142000,6824713373354000,743,"R+",120,743,"kworker/0:5",20371
+6824713373354000,0,79000,6824713373433000,670,"S",100,670,"kworker/u17:2",14944
+6824713373433000,0,170000,6824713373603000,743,"S",120,743,"kworker/0:5",20371
 6824713373603000,0,8922000,6824713382525000,0,"R",120,"[NULL]","swapper/5",0
-6824713373886000,2,518000,6824713374404000,7,"S",120,5,"adbd",20305
-6824713374404000,2,168000,6824713374572000,6,"R+",120,4,"rcu_preempt",7
-6824713374572000,2,494000,6824713375066000,8,"S",120,8,"rcuop/6",60
-6824713375066000,2,44000,6824713375110000,6,"S",120,4,"rcu_preempt",7
-6824713375110000,2,127000,6824713375237000,4,"R+",120,5,"UsbFfs-worker",20308
-6824713375237000,2,235000,6824713375472000,7,"S",120,5,"adbd",20305
-6824713375472000,2,230000,6824713375702000,4,"S",120,5,"UsbFfs-worker",20308
+6824713373886000,2,518000,6824713374404000,739,"S",120,739,"adbd",20305
+6824713374404000,2,168000,6824713374572000,5,"R+",120,5,"rcu_preempt",7
+6824713374572000,2,494000,6824713375066000,52,"S",120,52,"rcuop/6",60
+6824713375066000,2,44000,6824713375110000,5,"S",120,5,"rcu_preempt",7
+6824713375110000,2,127000,6824713375237000,2487,"R+",120,739,"UsbFfs-worker",20308
+6824713375237000,2,235000,6824713375472000,739,"S",120,739,"adbd",20305
+6824713375472000,2,230000,6824713375702000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713375702000,2,1343000,6824713377045000,0,"R",120,"[NULL]","swapper/5",0
-6824713377045000,2,741000,6824713377786000,16,"S",120,14,"kworker/u16:7",19422
+6824713377045000,2,741000,6824713377786000,702,"S",120,702,"kworker/u16:7",19422
 6824713377786000,2,2753000,6824713380539000,0,"R",120,"[NULL]","swapper/5",0
-6824713378015000,3,111000,6824713378126000,32,"S",120,30,"smem_native_rpm",87
+6824713378015000,3,111000,6824713378126000,77,"S",120,77,"smem_native_rpm",87
 6824713378126000,3,5664000,6824713383790000,0,"R",120,"[NULL]","swapper/5",0
-6824713380539000,2,133000,6824713380672000,26,"S",49,23,"sugov:0",605
-6824713380672000,2,103000,6824713380775000,6,"R+",120,4,"rcu_preempt",7
-6824713380775000,2,390000,6824713381165000,8,"S",120,8,"rcuop/6",60
-6824713381165000,2,41000,6824713381206000,6,"R+",120,4,"rcu_preempt",7
-6824713381206000,2,75000,6824713381281000,26,"S",49,23,"sugov:0",605
-6824713381281000,2,45000,6824713381326000,6,"S",120,4,"rcu_preempt",7
-6824713381295000,6,45000,6824713381340000,22,"S",49,20,"sugov:4",606
+6824713380539000,2,133000,6824713380672000,482,"S",49,482,"sugov:0",605
+6824713380672000,2,103000,6824713380775000,5,"R+",120,5,"rcu_preempt",7
+6824713380775000,2,390000,6824713381165000,52,"S",120,52,"rcuop/6",60
+6824713381165000,2,41000,6824713381206000,5,"R+",120,5,"rcu_preempt",7
+6824713381206000,2,75000,6824713381281000,482,"S",49,482,"sugov:0",605
+6824713381281000,2,45000,6824713381326000,5,"S",120,5,"rcu_preempt",7
+6824713381295000,6,45000,6824713381340000,483,"S",49,483,"sugov:4",606
 6824713381326000,2,1876000,6824713383202000,0,"R",120,"[NULL]","swapper/5",0
-6824713381340000,6,66000,6824713381406000,82,"S",120,5,"shell",20461
-6824713381406000,6,288000,6824713381694000,7,"S",120,5,"adbd",20305
-6824713381694000,6,1546000,6824713383240000,87,"R+",120,71,"ps",20464
-6824713382525000,0,114000,6824713382639000,79,"S",100,64,"kworker/u17:2",14944
-6824713382639000,0,173000,6824713382812000,35,"S",120,33,"kworker/0:5",20371
+6824713381340000,6,66000,6824713381406000,2728,"S",120,739,"shell",20461
+6824713381406000,6,288000,6824713381694000,739,"S",120,739,"adbd",20305
+6824713381694000,6,1546000,6824713383240000,2729,"R+",120,758,"ps",20464
+6824713382525000,0,114000,6824713382639000,670,"S",100,670,"kworker/u17:2",14944
+6824713382639000,0,173000,6824713382812000,743,"S",120,743,"kworker/0:5",20371
 6824713382812000,0,744000,6824713383556000,0,"R",120,"[NULL]","swapper/5",0
-6824713383202000,2,136000,6824713383338000,4,"S",120,5,"UsbFfs-worker",20308
-6824713383240000,6,170000,6824713383410000,22,"D",49,20,"sugov:4",606
+6824713383202000,2,136000,6824713383338000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713383240000,6,170000,6824713383410000,483,"D",49,483,"sugov:4",606
 6824713383338000,2,443000,6824713383781000,0,"R",120,"[NULL]","swapper/5",0
-6824713383410000,6,5051000,6824713388461000,87,"R+",120,71,"ps",20464
-6824713383556000,0,102000,6824713383658000,79,"S",100,64,"kworker/u17:2",14944
-6824713383658000,0,115000,6824713383773000,35,"R+",120,33,"kworker/0:5",20371
-6824713383773000,0,53000,6824713383826000,79,"S",100,64,"kworker/u17:2",14944
-6824713383781000,2,83000,6824713383864000,4,"S",120,5,"UsbFfs-worker",20308
-6824713383790000,3,60000,6824713383850000,32,"S",120,30,"smem_native_rpm",87
-6824713383826000,0,167000,6824713383993000,35,"S",120,33,"kworker/0:5",20371
+6824713383410000,6,5051000,6824713388461000,2729,"R+",120,758,"ps",20464
+6824713383556000,0,102000,6824713383658000,670,"S",100,670,"kworker/u17:2",14944
+6824713383658000,0,115000,6824713383773000,743,"R+",120,743,"kworker/0:5",20371
+6824713383773000,0,53000,6824713383826000,670,"S",100,670,"kworker/u17:2",14944
+6824713383781000,2,83000,6824713383864000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713383790000,3,60000,6824713383850000,77,"S",120,77,"smem_native_rpm",87
+6824713383826000,0,167000,6824713383993000,743,"S",120,743,"kworker/0:5",20371
 6824713383850000,3,11312000,6824713395162000,0,"R",120,"[NULL]","swapper/5",0
 6824713383864000,2,90000,6824713383954000,0,"R",120,"[NULL]","swapper/5",0
-6824713383954000,2,184000,6824713384138000,4,"R+",120,5,"UsbFfs-worker",20308
+6824713383954000,2,184000,6824713384138000,2487,"R+",120,739,"UsbFfs-worker",20308
 6824713383993000,0,6047000,6824713390040000,0,"R",120,"[NULL]","swapper/5",0
-6824713384138000,2,253000,6824713384391000,7,"S",120,5,"adbd",20305
-6824713384362000,4,24000,6824713384386000,22,"S",49,20,"sugov:4",606
+6824713384138000,2,253000,6824713384391000,739,"S",120,739,"adbd",20305
+6824713384362000,4,24000,6824713384386000,483,"S",49,483,"sugov:4",606
 6824713384386000,4,18375000,6824713402761000,0,"R",120,"[NULL]","swapper/5",0
-6824713384391000,2,105000,6824713384496000,4,"R+",120,5,"UsbFfs-worker",20308
-6824713384496000,2,149000,6824713384645000,7,"S",120,5,"adbd",20305
-6824713384645000,2,120000,6824713384765000,4,"S",120,5,"UsbFfs-worker",20308
+6824713384391000,2,105000,6824713384496000,2487,"R+",120,739,"UsbFfs-worker",20308
+6824713384496000,2,149000,6824713384645000,739,"S",120,739,"adbd",20305
+6824713384645000,2,120000,6824713384765000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713384765000,2,2176000,6824713386941000,0,"R",120,"[NULL]","swapper/5",0
-6824713386941000,2,96000,6824713387037000,6,"R+",120,4,"rcu_preempt",7
-6824713387037000,2,340000,6824713387377000,8,"S",120,8,"rcuop/6",60
-6824713387377000,2,41000,6824713387418000,6,"S",120,4,"rcu_preempt",7
+6824713386941000,2,96000,6824713387037000,5,"R+",120,5,"rcu_preempt",7
+6824713387037000,2,340000,6824713387377000,52,"S",120,52,"rcuop/6",60
+6824713387377000,2,41000,6824713387418000,5,"S",120,5,"rcu_preempt",7
 6824713387418000,2,3209000,6824713390627000,0,"R",120,"[NULL]","swapper/5",0
-6824713388461000,6,29000,6824713388490000,82,"S",120,5,"shell",20461
-6824713388490000,6,145000,6824713388635000,7,"S",120,5,"adbd",20305
-6824713388635000,6,3783000,6824713392418000,87,"R+",120,71,"ps",20464
-6824713390040000,0,142000,6824713390182000,79,"S",100,64,"kworker/u17:2",14944
-6824713390182000,0,178000,6824713390360000,35,"S",120,33,"kworker/0:5",20371
+6824713388461000,6,29000,6824713388490000,2728,"S",120,739,"shell",20461
+6824713388490000,6,145000,6824713388635000,739,"S",120,739,"adbd",20305
+6824713388635000,6,3783000,6824713392418000,2729,"R+",120,758,"ps",20464
+6824713390040000,0,142000,6824713390182000,670,"S",100,670,"kworker/u17:2",14944
+6824713390182000,0,178000,6824713390360000,743,"S",120,743,"kworker/0:5",20371
 6824713390360000,0,476000,6824713390836000,0,"R",120,"[NULL]","swapper/5",0
-6824713390627000,2,119000,6824713390746000,4,"S",120,5,"UsbFfs-worker",20308
+6824713390627000,2,119000,6824713390746000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713390746000,2,516000,6824713391262000,0,"R",120,"[NULL]","swapper/5",0
-6824713390836000,0,87000,6824713390923000,79,"S",100,64,"kworker/u17:2",14944
-6824713390923000,0,144000,6824713391067000,35,"S",120,33,"kworker/0:5",20371
+6824713390836000,0,87000,6824713390923000,670,"S",100,670,"kworker/u17:2",14944
+6824713390923000,0,144000,6824713391067000,743,"S",120,743,"kworker/0:5",20371
 6824713391067000,0,424000,6824713391491000,0,"R",120,"[NULL]","swapper/5",0
-6824713391262000,2,94000,6824713391356000,4,"S",120,5,"UsbFfs-worker",20308
+6824713391262000,2,94000,6824713391356000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713391356000,2,737000,6824713392093000,0,"R",120,"[NULL]","swapper/5",0
-6824713391491000,0,76000,6824713391567000,79,"S",100,64,"kworker/u17:2",14944
+6824713391491000,0,76000,6824713391567000,670,"S",100,670,"kworker/u17:2",14944
 6824713391567000,0,314000,6824713391881000,0,"R",120,"[NULL]","swapper/5",0
-6824713391881000,0,87000,6824713391968000,79,"S",100,64,"kworker/u17:2",14944
-6824713391968000,0,163000,6824713392131000,35,"S",120,33,"kworker/0:5",20371
-6824713392093000,2,134000,6824713392227000,4,"R+",120,5,"UsbFfs-worker",20308
+6824713391881000,0,87000,6824713391968000,670,"S",100,670,"kworker/u17:2",14944
+6824713391968000,0,163000,6824713392131000,743,"S",120,743,"kworker/0:5",20371
+6824713392093000,2,134000,6824713392227000,2487,"R+",120,739,"UsbFfs-worker",20308
 6824713392131000,0,1225000,6824713393356000,0,"R",120,"[NULL]","swapper/5",0
-6824713392227000,2,294000,6824713392521000,7,"S",120,5,"adbd",20305
-6824713392418000,6,21000,6824713392439000,82,"S",120,5,"shell",20461
-6824713392439000,6,7047000,6824713399486000,87,"R+",120,71,"ps",20464
-6824713392521000,2,105000,6824713392626000,4,"R+",120,5,"UsbFfs-worker",20308
-6824713392626000,2,812000,6824713393438000,7,"S",120,5,"adbd",20305
-6824713393356000,0,90000,6824713393446000,79,"S",100,64,"kworker/u17:2",14944
-6824713393438000,2,137000,6824713393575000,6,"S",120,4,"rcu_preempt",7
-6824713393446000,0,64000,6824713393510000,35,"S",120,33,"kworker/0:5",20371
+6824713392227000,2,294000,6824713392521000,739,"S",120,739,"adbd",20305
+6824713392418000,6,21000,6824713392439000,2728,"S",120,739,"shell",20461
+6824713392439000,6,7047000,6824713399486000,2729,"R+",120,758,"ps",20464
+6824713392521000,2,105000,6824713392626000,2487,"R+",120,739,"UsbFfs-worker",20308
+6824713392626000,2,812000,6824713393438000,739,"S",120,739,"adbd",20305
+6824713393356000,0,90000,6824713393446000,670,"S",100,670,"kworker/u17:2",14944
+6824713393438000,2,137000,6824713393575000,5,"S",120,5,"rcu_preempt",7
+6824713393446000,0,64000,6824713393510000,743,"S",120,743,"kworker/0:5",20371
 6824713393510000,0,41000,6824713393551000,0,"R",120,"[NULL]","swapper/5",0
-6824713393551000,0,67000,6824713393618000,79,"S",100,64,"kworker/u17:2",14944
-6824713393575000,2,258000,6824713393833000,4,"S",120,5,"UsbFfs-worker",20308
+6824713393551000,0,67000,6824713393618000,670,"S",100,670,"kworker/u17:2",14944
+6824713393575000,2,258000,6824713393833000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713393618000,0,173000,6824713393791000,0,"R",120,"[NULL]","swapper/5",0
-6824713393791000,0,72000,6824713393863000,79,"S",100,64,"kworker/u17:2",14944
+6824713393791000,0,72000,6824713393863000,670,"S",100,670,"kworker/u17:2",14944
 6824713393833000,2,136000,6824713393969000,0,"R",120,"[NULL]","swapper/5",0
-6824713393863000,0,110000,6824713393973000,35,"R+",120,33,"kworker/0:5",20371
-6824713393969000,2,167000,6824713394136000,4,"S",120,5,"UsbFfs-worker",20308
-6824713393973000,0,35000,6824713394008000,79,"S",100,64,"kworker/u17:2",14944
-6824713394008000,0,46000,6824713394054000,35,"S",120,33,"kworker/0:5",20371
+6824713393863000,0,110000,6824713393973000,743,"R+",120,743,"kworker/0:5",20371
+6824713393969000,2,167000,6824713394136000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713393973000,0,35000,6824713394008000,670,"S",100,670,"kworker/u17:2",14944
+6824713394008000,0,46000,6824713394054000,743,"S",120,743,"kworker/0:5",20371
 6824713394054000,0,41000,6824713394095000,0,"R",120,"[NULL]","swapper/5",0
-6824713394095000,0,67000,6824713394162000,79,"S",100,64,"kworker/u17:2",14944
+6824713394095000,0,67000,6824713394162000,670,"S",100,670,"kworker/u17:2",14944
 6824713394136000,2,347000,6824713394483000,0,"R",120,"[NULL]","swapper/5",0
 6824713394162000,0,179000,6824713394341000,0,"R",120,"[NULL]","swapper/5",0
-6824713394341000,0,59000,6824713394400000,79,"S",100,64,"kworker/u17:2",14944
-6824713394400000,0,224000,6824713394624000,35,"S",120,33,"kworker/0:5",20371
-6824713394483000,2,486000,6824713394969000,36,"S",111,34,"SDM_EventThread",685
+6824713394341000,0,59000,6824713394400000,670,"S",100,670,"kworker/u17:2",14944
+6824713394400000,0,224000,6824713394624000,743,"S",120,743,"kworker/0:5",20371
+6824713394483000,2,486000,6824713394969000,786,"S",111,494,"SDM_EventThread",685
 6824713394624000,0,46000,6824713394670000,0,"R",120,"[NULL]","swapper/5",0
-6824713394670000,0,199000,6824713394869000,40,"S",97,35,"DispSync",676
+6824713394670000,0,199000,6824713394869000,771,"S",97,493,"DispSync",676
 6824713394869000,0,625000,6824713395494000,0,"R",120,"[NULL]","swapper/5",0
-6824713394969000,2,86000,6824713395055000,4,"R+",120,5,"UsbFfs-worker",20308
-6824713395055000,2,184000,6824713395239000,7,"S",120,5,"adbd",20305
-6824713395078000,1,283000,6824713395361000,41,"S",97,35,"app",678
-6824713395162000,3,322000,6824713395484000,38,"S",120,35,"HwBinder:640_1",721
-6824713395239000,2,104000,6824713395343000,4,"R+",120,5,"UsbFfs-worker",20308
-6824713395343000,2,155000,6824713395498000,7,"S",120,5,"adbd",20305
+6824713394969000,2,86000,6824713395055000,2487,"R+",120,739,"UsbFfs-worker",20308
+6824713395055000,2,184000,6824713395239000,739,"S",120,739,"adbd",20305
+6824713395078000,1,283000,6824713395361000,773,"S",97,493,"app",678
+6824713395162000,3,322000,6824713395484000,777,"S",120,493,"HwBinder:640_1",721
+6824713395239000,2,104000,6824713395343000,2487,"R+",120,739,"UsbFfs-worker",20308
+6824713395343000,2,155000,6824713395498000,739,"S",120,739,"adbd",20305
 6824713395361000,1,1958000,6824713397319000,0,"R",120,"[NULL]","swapper/5",0
 6824713395484000,3,8157000,6824713403641000,0,"R",120,"[NULL]","swapper/5",0
-6824713395494000,0,49000,6824713395543000,40,"S",97,35,"DispSync",676
-6824713395498000,2,120000,6824713395618000,4,"S",120,5,"UsbFfs-worker",20308
-6824713395543000,0,1769000,6824713397312000,42,"S",120,36,"ndroid.systemui",1664
+6824713395494000,0,49000,6824713395543000,771,"S",97,493,"DispSync",676
+6824713395498000,2,120000,6824713395618000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713395543000,0,1769000,6824713397312000,644,"S",120,644,"ndroid.systemui",1664
 6824713395618000,2,7707000,6824713403325000,0,"R",120,"[NULL]","swapper/5",0
 6824713397312000,0,423000,6824713397735000,0,"R",120,"[NULL]","swapper/5",0
-6824713397319000,1,268000,6824713397587000,43,"S",120,35,"Binder:640_2",675
+6824713397319000,1,268000,6824713397587000,770,"S",120,493,"Binder:640_2",675
 6824713397587000,1,483000,6824713398070000,0,"R",120,"[NULL]","swapper/5",0
-6824713397735000,0,162000,6824713397897000,41,"S",97,35,"app",678
+6824713397735000,0,162000,6824713397897000,773,"S",97,493,"app",678
 6824713397897000,0,2323000,6824713400220000,0,"R",120,"[NULL]","swapper/5",0
-6824713398070000,1,74000,6824713398144000,40,"S",97,35,"DispSync",676
+6824713398070000,1,74000,6824713398144000,771,"S",97,493,"DispSync",676
 6824713398144000,1,3426000,6824713401570000,0,"R",120,"[NULL]","swapper/5",0
-6824713399486000,6,30000,6824713399516000,82,"S",120,5,"shell",20461
-6824713399516000,6,162000,6824713399678000,7,"S",120,5,"adbd",20305
-6824713399678000,6,3250000,6824713402928000,87,"R+",120,71,"ps",20464
-6824713400220000,0,111000,6824713400331000,79,"S",100,64,"kworker/u17:2",14944
-6824713400331000,0,120000,6824713400451000,35,"S",120,33,"kworker/0:5",20371
-6824713400451000,0,43000,6824713400494000,4,"R",120,5,"UsbFfs-worker",20308
-6824713400494000,0,70000,6824713400564000,79,"S",100,64,"kworker/u17:2",14944
-6824713400564000,0,40000,6824713400604000,35,"R+",120,33,"kworker/0:5",20371
-6824713400604000,0,34000,6824713400638000,79,"S",100,64,"kworker/u17:2",14944
-6824713400638000,0,17000,6824713400655000,35,"S",120,33,"kworker/0:5",20371
-6824713400655000,0,97000,6824713400752000,4,"S",120,5,"UsbFfs-worker",20308
-6824713400752000,0,71000,6824713400823000,79,"S",100,64,"kworker/u17:2",14944
+6824713399486000,6,30000,6824713399516000,2728,"S",120,739,"shell",20461
+6824713399516000,6,162000,6824713399678000,739,"S",120,739,"adbd",20305
+6824713399678000,6,3250000,6824713402928000,2729,"R+",120,758,"ps",20464
+6824713400220000,0,111000,6824713400331000,670,"S",100,670,"kworker/u17:2",14944
+6824713400331000,0,120000,6824713400451000,743,"S",120,743,"kworker/0:5",20371
+6824713400451000,0,43000,6824713400494000,2487,"R",120,739,"UsbFfs-worker",20308
+6824713400494000,0,70000,6824713400564000,670,"S",100,670,"kworker/u17:2",14944
+6824713400564000,0,40000,6824713400604000,743,"R+",120,743,"kworker/0:5",20371
+6824713400604000,0,34000,6824713400638000,670,"S",100,670,"kworker/u17:2",14944
+6824713400638000,0,17000,6824713400655000,743,"S",120,743,"kworker/0:5",20371
+6824713400655000,0,97000,6824713400752000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713400752000,0,71000,6824713400823000,670,"S",100,670,"kworker/u17:2",14944
 6824713400823000,0,363000,6824713401186000,0,"R",120,"[NULL]","swapper/5",0
-6824713401186000,0,78000,6824713401264000,79,"S",100,64,"kworker/u17:2",14944
-6824713401264000,0,103000,6824713401367000,35,"S",120,33,"kworker/0:5",20371
+6824713401186000,0,78000,6824713401264000,670,"S",100,670,"kworker/u17:2",14944
+6824713401264000,0,103000,6824713401367000,743,"S",120,743,"kworker/0:5",20371
 6824713401367000,0,329000,6824713401696000,0,"R",120,"[NULL]","swapper/5",0
-6824713401570000,1,237000,6824713401807000,4,"S",120,5,"UsbFfs-worker",20308
-6824713401696000,0,372000,6824713402068000,7,"S",120,5,"adbd",20305
+6824713401570000,1,237000,6824713401807000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713401696000,0,372000,6824713402068000,739,"S",120,739,"adbd",20305
 6824713401807000,1,2137000,6824713403944000,0,"R",120,"[NULL]","swapper/5",0
 6824713402068000,0,1411000,6824713403479000,0,"R",120,"[NULL]","swapper/5",0
-6824713402488000,7,51000,6824713402539000,82,"S",120,5,"shell",20461
+6824713402488000,7,51000,6824713402539000,2728,"S",120,739,"shell",20461
 6824713402539000,7,4230000,6824713406769000,0,"R",120,"[NULL]","swapper/5",0
-6824713402761000,4,165000,6824713402926000,7,"S",120,5,"adbd",20305
+6824713402761000,4,165000,6824713402926000,739,"S",120,739,"adbd",20305
 6824713402926000,4,350000,6824713403276000,0,"R",120,"[NULL]","swapper/5",0
-6824713402928000,6,24000,6824713402952000,89,"S",0,73,"migration/6",56
-6824713402952000,6,43000,6824713402995000,22,"S",49,20,"sugov:4",606
+6824713402928000,6,24000,6824713402952000,49,"S",0,49,"migration/6",56
+6824713402952000,6,43000,6824713402995000,483,"S",49,483,"sugov:4",606
 6824713402995000,6,4234000,6824713407229000,0,"R",120,"[NULL]","swapper/5",0
-6824713403276000,4,53289000,6824713456565000,87,"R",120,71,"ps",20464
-6824713403325000,2,97000,6824713403422000,25,"S",120,24,"ksoftirqd/2",25
+6824713403276000,4,53289000,6824713456565000,2729,"R",120,758,"ps",20464
+6824713403325000,2,97000,6824713403422000,22,"S",120,22,"ksoftirqd/2",25
 6824713403422000,2,498000,6824713403920000,0,"R",120,"[NULL]","swapper/5",0
-6824713403479000,0,104000,6824713403583000,79,"S",100,64,"kworker/u17:2",14944
-6824713403583000,0,99000,6824713403682000,35,"S",120,33,"kworker/0:5",20371
-6824713403641000,3,96000,6824713403737000,6,"S",120,4,"rcu_preempt",7
+6824713403479000,0,104000,6824713403583000,670,"S",100,670,"kworker/u17:2",14944
+6824713403583000,0,99000,6824713403682000,743,"S",120,743,"kworker/0:5",20371
+6824713403641000,3,96000,6824713403737000,5,"S",120,5,"rcu_preempt",7
 6824713403682000,0,507000,6824713404189000,0,"R",120,"[NULL]","swapper/5",0
 6824713403737000,3,885000,6824713404622000,0,"R",120,"[NULL]","swapper/5",0
-6824713403920000,2,530000,6824713404450000,8,"S",120,8,"rcuop/6",60
-6824713403944000,1,83000,6824713404027000,4,"S",120,5,"UsbFfs-worker",20308
+6824713403920000,2,530000,6824713404450000,52,"S",120,52,"rcuop/6",60
+6824713403944000,1,83000,6824713404027000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713404027000,1,662000,6824713404689000,0,"R",120,"[NULL]","swapper/5",0
-6824713404189000,0,132000,6824713404321000,79,"R+",100,64,"kworker/u17:2",14944
-6824713404321000,0,140000,6824713404461000,26,"S",49,23,"sugov:0",605
+6824713404189000,0,132000,6824713404321000,670,"R+",100,670,"kworker/u17:2",14944
+6824713404321000,0,140000,6824713404461000,482,"S",49,482,"sugov:0",605
 6824713404450000,2,572000,6824713405022000,0,"R",120,"[NULL]","swapper/5",0
-6824713404461000,0,55000,6824713404516000,79,"S",100,64,"kworker/u17:2",14944
-6824713404516000,0,125000,6824713404641000,35,"S",120,33,"kworker/0:5",20371
-6824713404622000,3,59000,6824713404681000,2,"S",100,2,"kworker/u17:1",1134
+6824713404461000,0,55000,6824713404516000,670,"S",100,670,"kworker/u17:2",14944
+6824713404516000,0,125000,6824713404641000,743,"S",120,743,"kworker/0:5",20371
+6824713404622000,3,59000,6824713404681000,630,"S",100,630,"kworker/u17:1",1134
 6824713404641000,0,3779000,6824713408420000,0,"R",120,"[NULL]","swapper/5",0
 6824713404681000,3,582000,6824713405263000,0,"R",120,"[NULL]","swapper/5",0
-6824713404689000,1,62000,6824713404751000,6,"S",120,4,"rcu_preempt",7
+6824713404689000,1,62000,6824713404751000,5,"S",120,5,"rcu_preempt",7
 6824713404751000,1,4130000,6824713408881000,0,"R",120,"[NULL]","swapper/5",0
-6824713405022000,2,120000,6824713405142000,4,"S",120,5,"UsbFfs-worker",20308
+6824713405022000,2,120000,6824713405142000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713405142000,2,330000,6824713405472000,0,"R",120,"[NULL]","swapper/5",0
-6824713405263000,3,117000,6824713405380000,2,"S",100,2,"kworker/u17:1",1134
-6824713405380000,3,138000,6824713405518000,49,"S",120,42,"kworker/3:1",17791
-6824713405472000,2,245000,6824713405717000,4,"S",120,5,"UsbFfs-worker",20308
+6824713405263000,3,117000,6824713405380000,630,"S",100,630,"kworker/u17:1",1134
+6824713405380000,3,138000,6824713405518000,686,"S",120,686,"kworker/3:1",17791
+6824713405472000,2,245000,6824713405717000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713405518000,3,2372000,6824713407890000,0,"R",120,"[NULL]","swapper/5",0
 6824713405717000,2,2451000,6824713408168000,0,"R",120,"[NULL]","swapper/5",0
-6824713405788000,5,62000,6824713405850000,7,"S",120,5,"adbd",20305
+6824713405788000,5,62000,6824713405850000,739,"S",120,739,"adbd",20305
 6824713405850000,5,1152000,6824713407002000,0,"R",120,"[NULL]","swapper/5",0
-6824713406769000,7,10000,6824713406779000,9,"S",120,6,"rcuop/4",44
+6824713406769000,7,10000,6824713406779000,38,"S",120,38,"rcuop/4",44
 6824713406779000,7,26544000,6824713433323000,0,"R",120,"[NULL]","swapper/5",0
-6824713407002000,5,29000,6824713407031000,82,"S",120,5,"shell",20461
+6824713407002000,5,29000,6824713407031000,2728,"S",120,739,"shell",20461
 6824713407031000,5,7986000,6824713415017000,0,"R",120,"[NULL]","swapper/5",0
-6824713407229000,6,96000,6824713407325000,7,"S",120,5,"adbd",20305
+6824713407229000,6,96000,6824713407325000,739,"S",120,739,"adbd",20305
 6824713407325000,6,1876000,6824713409201000,0,"R",120,"[NULL]","swapper/5",0
-6824713407890000,3,127000,6824713408017000,2,"S",100,2,"kworker/u17:1",1134
-6824713408017000,3,114000,6824713408131000,49,"S",120,42,"kworker/3:1",17791
+6824713407890000,3,127000,6824713408017000,630,"S",100,630,"kworker/u17:1",1134
+6824713408017000,3,114000,6824713408131000,686,"S",120,686,"kworker/3:1",17791
 6824713408131000,3,8216000,6824713416347000,0,"R",120,"[NULL]","swapper/5",0
-6824713408168000,2,97000,6824713408265000,4,"S",120,5,"UsbFfs-worker",20308
+6824713408168000,2,97000,6824713408265000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713408265000,2,326000,6824713408591000,0,"R",120,"[NULL]","swapper/5",0
-6824713408420000,0,106000,6824713408526000,2,"S",100,2,"kworker/u17:1",1134
-6824713408526000,0,129000,6824713408655000,35,"S",120,33,"kworker/0:5",20371
-6824713408591000,2,198000,6824713408789000,2,"S",100,2,"kworker/u17:1",1134
+6824713408420000,0,106000,6824713408526000,630,"S",100,630,"kworker/u17:1",1134
+6824713408526000,0,129000,6824713408655000,743,"S",120,743,"kworker/0:5",20371
+6824713408591000,2,198000,6824713408789000,630,"S",100,630,"kworker/u17:1",1134
 6824713408655000,0,53000,6824713408708000,0,"R",120,"[NULL]","swapper/5",0
-6824713408708000,0,44000,6824713408752000,79,"S",100,64,"kworker/u17:2",14944
+6824713408708000,0,44000,6824713408752000,670,"S",100,670,"kworker/u17:2",14944
 6824713408752000,0,7418000,6824713416170000,0,"R",120,"[NULL]","swapper/5",0
-6824713408789000,2,105000,6824713408894000,31,"S",120,29,"kworker/2:0",18823
-6824713408881000,1,261000,6824713409142000,4,"S",120,5,"UsbFfs-worker",20308
+6824713408789000,2,105000,6824713408894000,694,"S",120,694,"kworker/2:0",18823
+6824713408881000,1,261000,6824713409142000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713408894000,2,1471000,6824713410365000,0,"R",120,"[NULL]","swapper/5",0
 6824713409142000,1,888000,6824713410030000,0,"R",120,"[NULL]","swapper/5",0
-6824713409201000,6,60000,6824713409261000,7,"S",120,5,"adbd",20305
+6824713409201000,6,60000,6824713409261000,739,"S",120,739,"adbd",20305
 6824713409261000,6,6003000,6824713415264000,0,"R",120,"[NULL]","swapper/5",0
-6824713410030000,1,92000,6824713410122000,6,"S",120,4,"rcu_preempt",7
+6824713410030000,1,92000,6824713410122000,5,"S",120,5,"rcu_preempt",7
 6824713410122000,1,6146000,6824713416268000,0,"R",120,"[NULL]","swapper/5",0
-6824713410365000,2,1409000,6824713411774000,8,"S",120,8,"rcuop/6",60
+6824713410365000,2,1409000,6824713411774000,52,"S",120,52,"rcuop/6",60
 6824713411774000,2,3953000,6824713415727000,0,"R",120,"[NULL]","swapper/5",0
-6824713415017000,5,41000,6824713415058000,82,"S",120,5,"shell",20461
+6824713415017000,5,41000,6824713415058000,2728,"S",120,739,"shell",20461
 6824713415058000,5,871000,6824713415929000,0,"R",120,"[NULL]","swapper/5",0
-6824713415264000,6,114000,6824713415378000,7,"S",120,5,"adbd",20305
+6824713415264000,6,114000,6824713415378000,739,"S",120,739,"adbd",20305
 6824713415378000,6,2157000,6824713417535000,0,"R",120,"[NULL]","swapper/5",0
-6824713415727000,2,177000,6824713415904000,2,"S",100,2,"kworker/u17:1",1134
-6824713415904000,2,176000,6824713416080000,31,"S",120,29,"kworker/2:0",18823
-6824713415929000,5,22000,6824713415951000,82,"S",120,5,"shell",20461
+6824713415727000,2,177000,6824713415904000,630,"S",100,630,"kworker/u17:1",1134
+6824713415904000,2,176000,6824713416080000,694,"S",120,694,"kworker/2:0",18823
+6824713415929000,5,22000,6824713415951000,2728,"S",120,739,"shell",20461
 6824713415951000,5,4014000,6824713419965000,0,"R",120,"[NULL]","swapper/5",0
 6824713416080000,2,579000,6824713416659000,0,"R",120,"[NULL]","swapper/5",0
-6824713416170000,0,101000,6824713416271000,79,"S",100,64,"kworker/u17:2",14944
-6824713416268000,1,135000,6824713416403000,4,"S",120,5,"UsbFfs-worker",20308
-6824713416271000,0,81000,6824713416352000,35,"S",120,33,"kworker/0:5",20371
-6824713416347000,3,56000,6824713416403000,2,"S",100,2,"kworker/u17:1",1134
+6824713416170000,0,101000,6824713416271000,670,"S",100,670,"kworker/u17:2",14944
+6824713416268000,1,135000,6824713416403000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713416271000,0,81000,6824713416352000,743,"S",120,743,"kworker/0:5",20371
+6824713416347000,3,56000,6824713416403000,630,"S",100,630,"kworker/u17:1",1134
 6824713416352000,0,4993000,6824713421345000,0,"R",120,"[NULL]","swapper/5",0
 6824713416403000,3,2160000,6824713418563000,0,"R",120,"[NULL]","swapper/5",0
 6824713416403000,1,272000,6824713416675000,0,"R",120,"[NULL]","swapper/5",0
-6824713416659000,2,84000,6824713416743000,2,"S",100,2,"kworker/u17:1",1134
-6824713416675000,1,57000,6824713416732000,6,"S",120,4,"rcu_preempt",7
+6824713416659000,2,84000,6824713416743000,630,"S",100,630,"kworker/u17:1",1134
+6824713416675000,1,57000,6824713416732000,5,"S",120,5,"rcu_preempt",7
 6824713416732000,1,521000,6824713417253000,0,"R",120,"[NULL]","swapper/5",0
 6824713416743000,2,74000,6824713416817000,0,"R",120,"[NULL]","swapper/5",0
-6824713416817000,2,91000,6824713416908000,2,"S",100,2,"kworker/u17:1",1134
-6824713416908000,2,119000,6824713417027000,31,"S",120,29,"kworker/2:0",18823
+6824713416817000,2,91000,6824713416908000,630,"S",100,630,"kworker/u17:1",1134
+6824713416908000,2,119000,6824713417027000,694,"S",120,694,"kworker/2:0",18823
 6824713417027000,2,1013000,6824713418040000,0,"R",120,"[NULL]","swapper/5",0
-6824713417253000,1,234000,6824713417487000,4,"S",120,5,"UsbFfs-worker",20308
+6824713417253000,1,234000,6824713417487000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713417487000,1,1008000,6824713418495000,0,"R",120,"[NULL]","swapper/5",0
-6824713417535000,6,118000,6824713417653000,7,"S",120,5,"adbd",20305
+6824713417535000,6,118000,6824713417653000,739,"S",120,739,"adbd",20305
 6824713417653000,6,1707000,6824713419360000,0,"R",120,"[NULL]","swapper/5",0
-6824713418040000,2,115000,6824713418155000,2,"S",100,2,"kworker/u17:1",1134
-6824713418155000,2,178000,6824713418333000,31,"S",120,29,"kworker/2:0",18823
+6824713418040000,2,115000,6824713418155000,630,"S",100,630,"kworker/u17:1",1134
+6824713418155000,2,178000,6824713418333000,694,"S",120,694,"kworker/2:0",18823
 6824713418333000,2,395000,6824713418728000,0,"R",120,"[NULL]","swapper/5",0
-6824713418495000,1,107000,6824713418602000,4,"S",120,5,"UsbFfs-worker",20308
-6824713418563000,3,106000,6824713418669000,2,"S",100,2,"kworker/u17:1",1134
+6824713418495000,1,107000,6824713418602000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713418563000,3,106000,6824713418669000,630,"S",100,630,"kworker/u17:1",1134
 6824713418602000,1,455000,6824713419057000,0,"R",120,"[NULL]","swapper/5",0
-6824713418669000,3,117000,6824713418786000,49,"S",120,42,"kworker/3:1",17791
-6824713418728000,2,76000,6824713418804000,2,"S",100,2,"kworker/u17:1",1134
+6824713418669000,3,117000,6824713418786000,686,"S",120,686,"kworker/3:1",17791
+6824713418728000,2,76000,6824713418804000,630,"S",100,630,"kworker/u17:1",1134
 6824713418786000,3,2669000,6824713421455000,0,"R",120,"[NULL]","swapper/5",0
 6824713418804000,2,62000,6824713418866000,0,"R",120,"[NULL]","swapper/5",0
-6824713418866000,2,86000,6824713418952000,2,"S",100,2,"kworker/u17:1",1134
-6824713418952000,2,87000,6824713419039000,31,"S",120,29,"kworker/2:0",18823
+6824713418866000,2,86000,6824713418952000,630,"S",100,630,"kworker/u17:1",1134
+6824713418952000,2,87000,6824713419039000,694,"S",120,694,"kworker/2:0",18823
 6824713419039000,2,1615000,6824713420654000,0,"R",120,"[NULL]","swapper/5",0
-6824713419057000,1,251000,6824713419308000,4,"S",120,5,"UsbFfs-worker",20308
+6824713419057000,1,251000,6824713419308000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713419308000,1,1797000,6824713421105000,0,"R",120,"[NULL]","swapper/5",0
-6824713419360000,6,50000,6824713419410000,7,"S",120,5,"adbd",20305
+6824713419360000,6,50000,6824713419410000,739,"S",120,739,"adbd",20305
 6824713419410000,6,798000,6824713420208000,0,"R",120,"[NULL]","swapper/5",0
-6824713419965000,5,29000,6824713419994000,82,"S",120,5,"shell",20461
+6824713419965000,5,29000,6824713419994000,2728,"S",120,739,"shell",20461
 6824713419994000,5,3214000,6824713423208000,0,"R",120,"[NULL]","swapper/5",0
-6824713420208000,6,90000,6824713420298000,7,"S",120,5,"adbd",20305
+6824713420208000,6,90000,6824713420298000,739,"S",120,739,"adbd",20305
 6824713420298000,6,1655000,6824713421953000,0,"R",120,"[NULL]","swapper/5",0
-6824713420654000,2,115000,6824713420769000,2,"S",100,2,"kworker/u17:1",1134
-6824713420769000,2,111000,6824713420880000,31,"S",120,29,"kworker/2:0",18823
+6824713420654000,2,115000,6824713420769000,630,"S",100,630,"kworker/u17:1",1134
+6824713420769000,2,111000,6824713420880000,694,"S",120,694,"kworker/2:0",18823
 6824713420880000,2,66000,6824713420946000,0,"R",120,"[NULL]","swapper/5",0
-6824713420946000,2,147000,6824713421093000,2,"S",100,2,"kworker/u17:1",1134
-6824713421093000,2,133000,6824713421226000,31,"S",120,29,"kworker/2:0",18823
-6824713421105000,1,129000,6824713421234000,4,"S",120,5,"UsbFfs-worker",20308
+6824713420946000,2,147000,6824713421093000,630,"S",100,630,"kworker/u17:1",1134
+6824713421093000,2,133000,6824713421226000,694,"S",120,694,"kworker/2:0",18823
+6824713421105000,1,129000,6824713421234000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713421226000,2,3867000,6824713425093000,0,"R",120,"[NULL]","swapper/5",0
 6824713421234000,1,424000,6824713421658000,0,"R",120,"[NULL]","swapper/5",0
-6824713421345000,0,93000,6824713421438000,79,"S",100,64,"kworker/u17:2",14944
+6824713421345000,0,93000,6824713421438000,670,"S",100,670,"kworker/u17:2",14944
 6824713421438000,0,49000,6824713421487000,0,"R",120,"[NULL]","swapper/5",0
-6824713421455000,3,119000,6824713421574000,2,"S",100,2,"kworker/u17:1",1134
-6824713421487000,0,43000,6824713421530000,79,"S",100,64,"kworker/u17:2",14944
+6824713421455000,3,119000,6824713421574000,630,"S",100,630,"kworker/u17:1",1134
+6824713421487000,0,43000,6824713421530000,670,"S",100,670,"kworker/u17:2",14944
 6824713421530000,0,6608000,6824713428138000,0,"R",120,"[NULL]","swapper/5",0
-6824713421574000,3,130000,6824713421704000,49,"S",120,42,"kworker/3:1",17791
-6824713421658000,1,224000,6824713421882000,4,"S",120,5,"UsbFfs-worker",20308
+6824713421574000,3,130000,6824713421704000,686,"S",120,686,"kworker/3:1",17791
+6824713421658000,1,224000,6824713421882000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713421704000,3,2240000,6824713423944000,0,"R",120,"[NULL]","swapper/5",0
 6824713421882000,1,1476000,6824713423358000,0,"R",120,"[NULL]","swapper/5",0
-6824713421953000,6,52000,6824713422005000,7,"S",120,5,"adbd",20305
+6824713421953000,6,52000,6824713422005000,739,"S",120,739,"adbd",20305
 6824713422005000,6,1245000,6824713423250000,0,"R",120,"[NULL]","swapper/5",0
-6824713423208000,5,44000,6824713423252000,82,"S",120,5,"shell",20461
-6824713423250000,6,81000,6824713423331000,7,"S",120,5,"adbd",20305
+6824713423208000,5,44000,6824713423252000,2728,"S",120,739,"shell",20461
+6824713423250000,6,81000,6824713423331000,739,"S",120,739,"adbd",20305
 6824713423252000,5,4584000,6824713427836000,0,"R",120,"[NULL]","swapper/5",0
 6824713423331000,6,90000,6824713423421000,0,"R",120,"[NULL]","swapper/5",0
-6824713423358000,1,94000,6824713423452000,6,"S",120,4,"rcu_preempt",7
-6824713423421000,6,90000,6824713423511000,9,"S",120,6,"rcuop/4",44
+6824713423358000,1,94000,6824713423452000,5,"S",120,5,"rcu_preempt",7
+6824713423421000,6,90000,6824713423511000,38,"S",120,38,"rcuop/4",44
 6824713423452000,1,101000,6824713423553000,0,"R",120,"[NULL]","swapper/5",0
 6824713423511000,6,2065000,6824713425576000,0,"R",120,"[NULL]","swapper/5",0
-6824713423553000,1,47000,6824713423600000,6,"S",120,4,"rcu_preempt",7
+6824713423553000,1,47000,6824713423600000,5,"S",120,5,"rcu_preempt",7
 6824713423600000,1,800000,6824713424400000,0,"R",120,"[NULL]","swapper/5",0
-6824713423944000,3,121000,6824713424065000,2,"S",100,2,"kworker/u17:1",1134
-6824713424065000,3,113000,6824713424178000,49,"S",120,42,"kworker/3:1",17791
+6824713423944000,3,121000,6824713424065000,630,"S",100,630,"kworker/u17:1",1134
+6824713424065000,3,113000,6824713424178000,686,"S",120,686,"kworker/3:1",17791
 6824713424178000,3,631000,6824713424809000,0,"R",120,"[NULL]","swapper/5",0
-6824713424400000,1,98000,6824713424498000,4,"S",120,5,"UsbFfs-worker",20308
+6824713424400000,1,98000,6824713424498000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713424498000,1,773000,6824713425271000,0,"R",120,"[NULL]","swapper/5",0
-6824713424809000,3,127000,6824713424936000,2,"S",100,2,"kworker/u17:1",1134
-6824713424936000,3,111000,6824713425047000,49,"S",120,42,"kworker/3:1",17791
+6824713424809000,3,127000,6824713424936000,630,"S",100,630,"kworker/u17:1",1134
+6824713424936000,3,111000,6824713425047000,686,"S",120,686,"kworker/3:1",17791
 6824713425047000,3,3858000,6824713428905000,0,"R",120,"[NULL]","swapper/5",0
-6824713425093000,2,105000,6824713425198000,2,"S",100,2,"kworker/u17:1",1134
-6824713425198000,2,89000,6824713425287000,31,"S",120,29,"kworker/2:0",18823
-6824713425271000,1,260000,6824713425531000,4,"S",120,5,"UsbFfs-worker",20308
+6824713425093000,2,105000,6824713425198000,630,"S",100,630,"kworker/u17:1",1134
+6824713425198000,2,89000,6824713425287000,694,"S",120,694,"kworker/2:0",18823
+6824713425271000,1,260000,6824713425531000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713425287000,2,1587000,6824713426874000,0,"R",120,"[NULL]","swapper/5",0
 6824713425531000,1,3451000,6824713428982000,0,"R",120,"[NULL]","swapper/5",0
-6824713425576000,6,50000,6824713425626000,7,"S",120,5,"adbd",20305
+6824713425576000,6,50000,6824713425626000,739,"S",120,739,"adbd",20305
 6824713425626000,6,2459000,6824713428085000,0,"R",120,"[NULL]","swapper/5",0
-6824713426874000,2,103000,6824713426977000,16,"S",120,14,"kworker/u16:7",19422
+6824713426874000,2,103000,6824713426977000,702,"S",120,702,"kworker/u16:7",19422
 6824713426977000,2,1675000,6824713428652000,0,"R",120,"[NULL]","swapper/5",0
-6824713427836000,5,29000,6824713427865000,82,"S",120,5,"shell",20461
+6824713427836000,5,29000,6824713427865000,2728,"S",120,739,"shell",20461
 6824713427865000,5,2265000,6824713430130000,0,"R",120,"[NULL]","swapper/5",0
-6824713428085000,6,99000,6824713428184000,7,"S",120,5,"adbd",20305
-6824713428138000,0,104000,6824713428242000,35,"S",120,33,"kworker/0:5",20371
+6824713428085000,6,99000,6824713428184000,739,"S",120,739,"adbd",20305
+6824713428138000,0,104000,6824713428242000,743,"S",120,743,"kworker/0:5",20371
 6824713428184000,6,2870000,6824713431054000,0,"R",120,"[NULL]","swapper/5",0
 6824713428242000,0,898000,6824713429140000,0,"R",120,"[NULL]","swapper/5",0
-6824713428652000,2,437000,6824713429089000,36,"S",111,34,"SDM_EventThread",685
-6824713428905000,3,123000,6824713429028000,2,"S",100,2,"kworker/u17:1",1134
-6824713428982000,1,322000,6824713429304000,38,"S",120,35,"HwBinder:640_1",721
-6824713429028000,3,118000,6824713429146000,49,"S",120,42,"kworker/3:1",17791
+6824713428652000,2,437000,6824713429089000,786,"S",111,494,"SDM_EventThread",685
+6824713428905000,3,123000,6824713429028000,630,"S",100,630,"kworker/u17:1",1134
+6824713428982000,1,322000,6824713429304000,777,"S",120,493,"HwBinder:640_1",721
+6824713429028000,3,118000,6824713429146000,686,"S",120,686,"kworker/3:1",17791
 6824713429089000,2,544000,6824713429633000,0,"R",120,"[NULL]","swapper/5",0
-6824713429140000,0,196000,6824713429336000,40,"S",97,35,"DispSync",676
+6824713429140000,0,196000,6824713429336000,771,"S",97,493,"DispSync",676
 6824713429146000,3,734000,6824713429880000,0,"R",120,"[NULL]","swapper/5",0
-6824713429304000,1,93000,6824713429397000,4,"S",120,5,"UsbFfs-worker",20308
+6824713429304000,1,93000,6824713429397000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713429336000,0,735000,6824713430071000,0,"R",120,"[NULL]","swapper/5",0
 6824713429397000,1,470000,6824713429867000,0,"R",120,"[NULL]","swapper/5",0
-6824713429633000,2,287000,6824713429920000,41,"S",97,35,"app",678
-6824713429867000,1,193000,6824713430060000,40,"S",97,35,"DispSync",676
-6824713429880000,3,203000,6824713430083000,2,"S",100,2,"kworker/u17:1",1134
+6824713429633000,2,287000,6824713429920000,773,"S",97,493,"app",678
+6824713429867000,1,193000,6824713430060000,771,"S",97,493,"DispSync",676
+6824713429880000,3,203000,6824713430083000,630,"S",100,630,"kworker/u17:1",1134
 6824713429920000,2,86000,6824713430006000,0,"R",120,"[NULL]","swapper/5",0
-6824713430006000,2,61000,6824713430067000,6,"S",120,4,"rcu_preempt",7
+6824713430006000,2,61000,6824713430067000,5,"S",120,5,"rcu_preempt",7
 6824713430060000,1,492000,6824713430552000,0,"R",120,"[NULL]","swapper/5",0
 6824713430067000,2,113000,6824713430180000,0,"R",120,"[NULL]","swapper/5",0
-6824713430071000,0,1790000,6824713431861000,42,"S",120,36,"ndroid.systemui",1664
-6824713430083000,3,143000,6824713430226000,49,"S",120,42,"kworker/3:1",17791
-6824713430130000,5,24000,6824713430154000,82,"S",120,5,"shell",20461
+6824713430071000,0,1790000,6824713431861000,644,"S",120,644,"ndroid.systemui",1664
+6824713430083000,3,143000,6824713430226000,686,"S",120,686,"kworker/3:1",17791
+6824713430130000,5,24000,6824713430154000,2728,"S",120,739,"shell",20461
 6824713430154000,5,3393000,6824713433547000,0,"R",120,"[NULL]","swapper/5",0
-6824713430180000,2,112000,6824713430292000,4,"S",120,5,"UsbFfs-worker",20308
+6824713430180000,2,112000,6824713430292000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713430226000,3,1706000,6824713431932000,0,"R",120,"[NULL]","swapper/5",0
 6824713430292000,2,469000,6824713430761000,0,"R",120,"[NULL]","swapper/5",0
-6824713430552000,1,120000,6824713430672000,2,"S",100,2,"kworker/u17:1",1134
-6824713430672000,1,134000,6824713430806000,48,"S",120,41,"kworker/1:1",18800
-6824713430761000,2,222000,6824713430983000,4,"S",120,5,"UsbFfs-worker",20308
+6824713430552000,1,120000,6824713430672000,630,"S",100,630,"kworker/u17:1",1134
+6824713430672000,1,134000,6824713430806000,693,"S",120,693,"kworker/1:1",18800
+6824713430761000,2,222000,6824713430983000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713430806000,1,695000,6824713431501000,0,"R",120,"[NULL]","swapper/5",0
 6824713430983000,2,607000,6824713431590000,0,"R",120,"[NULL]","swapper/5",0
-6824713431054000,6,118000,6824713431172000,7,"S",120,5,"adbd",20305
+6824713431054000,6,118000,6824713431172000,739,"S",120,739,"adbd",20305
 6824713431172000,6,1852000,6824713433024000,0,"R",120,"[NULL]","swapper/5",0
-6824713431501000,1,118000,6824713431619000,2,"S",100,2,"kworker/u17:1",1134
-6824713431590000,2,264000,6824713431854000,43,"R+",120,35,"Binder:640_2",675
-6824713431619000,1,101000,6824713431720000,48,"S",120,41,"kworker/1:1",18800
-6824713431720000,1,103000,6824713431823000,4,"S",120,5,"UsbFfs-worker",20308
+6824713431501000,1,118000,6824713431619000,630,"S",100,630,"kworker/u17:1",1134
+6824713431590000,2,264000,6824713431854000,770,"R+",120,493,"Binder:640_2",675
+6824713431619000,1,101000,6824713431720000,693,"S",120,693,"kworker/1:1",18800
+6824713431720000,1,103000,6824713431823000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713431823000,1,758000,6824713432581000,0,"R",120,"[NULL]","swapper/5",0
-6824713431854000,2,46000,6824713431900000,41,"S",97,35,"app",678
+6824713431854000,2,46000,6824713431900000,773,"S",97,493,"app",678
 6824713431861000,0,492000,6824713432353000,0,"R",120,"[NULL]","swapper/5",0
-6824713431900000,2,159000,6824713432059000,43,"S",120,35,"Binder:640_2",675
-6824713431932000,3,91000,6824713432023000,2,"S",100,2,"kworker/u17:1",1134
+6824713431900000,2,159000,6824713432059000,770,"S",120,493,"Binder:640_2",675
+6824713431932000,3,91000,6824713432023000,630,"S",100,630,"kworker/u17:1",1134
 6824713432023000,3,128000,6824713432151000,0,"R",120,"[NULL]","swapper/5",0
 6824713432059000,2,655000,6824713432714000,0,"R",120,"[NULL]","swapper/5",0
-6824713432151000,3,92000,6824713432243000,2,"S",100,2,"kworker/u17:1",1134
-6824713432243000,3,110000,6824713432353000,49,"S",120,42,"kworker/3:1",17791
+6824713432151000,3,92000,6824713432243000,630,"S",100,630,"kworker/u17:1",1134
+6824713432243000,3,110000,6824713432353000,686,"S",120,686,"kworker/3:1",17791
 6824713432353000,3,349000,6824713432702000,0,"R",120,"[NULL]","swapper/5",0
-6824713432353000,0,196000,6824713432549000,41,"S",97,35,"app",678
+6824713432353000,0,196000,6824713432549000,773,"S",97,493,"app",678
 6824713432549000,0,28791000,6824713461340000,0,"R",120,"[NULL]","swapper/5",0
-6824713432581000,1,110000,6824713432691000,4,"S",120,5,"UsbFfs-worker",20308
+6824713432581000,1,110000,6824713432691000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713432691000,1,29017000,6824713461708000,0,"R",120,"[NULL]","swapper/5",0
-6824713432702000,3,77000,6824713432779000,2,"S",100,2,"kworker/u17:1",1134
-6824713432714000,2,166000,6824713432880000,40,"S",97,35,"DispSync",676
+6824713432702000,3,77000,6824713432779000,630,"S",100,630,"kworker/u17:1",1134
+6824713432714000,2,166000,6824713432880000,771,"S",97,493,"DispSync",676
 6824713432779000,3,31370000,6824713464149000,0,"R",120,"[NULL]","swapper/5",0
 6824713432880000,2,3801000,6824713436681000,0,"R",120,"[NULL]","swapper/5",0
-6824713433024000,6,46000,6824713433070000,2,"S",100,2,"kworker/u17:1",1134
-6824713433070000,6,31000,6824713433101000,3,"S",120,3,"kworker/6:1",14833
+6824713433024000,6,46000,6824713433070000,630,"S",100,630,"kworker/u17:1",1134
+6824713433070000,6,31000,6824713433101000,669,"S",120,669,"kworker/6:1",14833
 6824713433101000,6,1961000,6824713435062000,0,"R",120,"[NULL]","swapper/5",0
-6824713433323000,7,68000,6824713433391000,4,"S",120,5,"UsbFfs-worker",20308
+6824713433323000,7,68000,6824713433391000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713433391000,7,2508000,6824713435899000,0,"R",120,"[NULL]","swapper/5",0
-6824713433547000,5,47000,6824713433594000,7,"S",120,5,"adbd",20305
+6824713433547000,5,47000,6824713433594000,739,"S",120,739,"adbd",20305
 6824713433594000,5,1241000,6824713434835000,0,"R",120,"[NULL]","swapper/5",0
-6824713434835000,5,30000,6824713434865000,82,"S",120,5,"shell",20461
+6824713434835000,5,30000,6824713434865000,2728,"S",120,739,"shell",20461
 6824713434865000,5,2740000,6824713437605000,0,"R",120,"[NULL]","swapper/5",0
-6824713435062000,6,101000,6824713435163000,7,"S",120,5,"adbd",20305
+6824713435062000,6,101000,6824713435163000,739,"S",120,739,"adbd",20305
 6824713435163000,6,471000,6824713435634000,0,"R",120,"[NULL]","swapper/5",0
-6824713435634000,6,36000,6824713435670000,2,"S",100,2,"kworker/u17:1",1134
-6824713435670000,6,13000,6824713435683000,3,"S",120,3,"kworker/6:1",14833
+6824713435634000,6,36000,6824713435670000,630,"S",100,630,"kworker/u17:1",1134
+6824713435670000,6,13000,6824713435683000,669,"S",120,669,"kworker/6:1",14833
 6824713435683000,6,316000,6824713435999000,0,"R",120,"[NULL]","swapper/5",0
-6824713435899000,7,15000,6824713435914000,4,"S",120,5,"UsbFfs-worker",20308
+6824713435899000,7,15000,6824713435914000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713435914000,7,346000,6824713436260000,0,"R",120,"[NULL]","swapper/5",0
-6824713435999000,6,24000,6824713436023000,2,"S",100,2,"kworker/u17:1",1134
-6824713436023000,6,13000,6824713436036000,3,"S",120,3,"kworker/6:1",14833
+6824713435999000,6,24000,6824713436023000,630,"S",100,630,"kworker/u17:1",1134
+6824713436023000,6,13000,6824713436036000,669,"S",120,669,"kworker/6:1",14833
 6824713436036000,6,273000,6824713436309000,0,"R",120,"[NULL]","swapper/5",0
-6824713436260000,7,20000,6824713436280000,4,"S",120,5,"UsbFfs-worker",20308
+6824713436260000,7,20000,6824713436280000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713436280000,7,302000,6824713436582000,0,"R",120,"[NULL]","swapper/5",0
-6824713436309000,6,29000,6824713436338000,2,"S",100,2,"kworker/u17:1",1134
+6824713436309000,6,29000,6824713436338000,630,"S",100,630,"kworker/u17:1",1134
 6824713436338000,6,35000,6824713436373000,0,"R",120,"[NULL]","swapper/5",0
-6824713436373000,6,29000,6824713436402000,2,"S",100,2,"kworker/u17:1",1134
-6824713436402000,6,14000,6824713436416000,3,"S",120,3,"kworker/6:1",14833
+6824713436373000,6,29000,6824713436402000,630,"S",100,630,"kworker/u17:1",1134
+6824713436402000,6,14000,6824713436416000,669,"S",120,669,"kworker/6:1",14833
 6824713436416000,6,184000,6824713436600000,0,"R",120,"[NULL]","swapper/5",0
-6824713436582000,7,40000,6824713436622000,4,"S",120,5,"UsbFfs-worker",20308
-6824713436600000,6,53000,6824713436653000,7,"S",120,5,"adbd",20305
+6824713436582000,7,40000,6824713436622000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713436600000,6,53000,6824713436653000,739,"S",120,739,"adbd",20305
 6824713436622000,7,1360000,6824713437982000,0,"R",120,"[NULL]","swapper/5",0
 6824713436653000,6,83000,6824713436736000,0,"R",120,"[NULL]","swapper/5",0
-6824713436681000,2,89000,6824713436770000,6,"S",120,4,"rcu_preempt",7
-6824713436736000,6,216000,6824713436952000,9,"S",120,6,"rcuop/4",44
+6824713436681000,2,89000,6824713436770000,5,"S",120,5,"rcu_preempt",7
+6824713436736000,6,216000,6824713436952000,38,"S",120,38,"rcuop/4",44
 6824713436770000,2,222000,6824713436992000,0,"R",120,"[NULL]","swapper/5",0
 6824713436952000,6,678000,6824713437630000,0,"R",120,"[NULL]","swapper/5",0
-6824713436992000,2,48000,6824713437040000,6,"S",120,4,"rcu_preempt",7
+6824713436992000,2,48000,6824713437040000,5,"S",120,5,"rcu_preempt",7
 6824713437040000,2,6321000,6824713443361000,0,"R",120,"[NULL]","swapper/5",0
-6824713437605000,5,30000,6824713437635000,82,"S",120,5,"shell",20461
-6824713437630000,6,82000,6824713437712000,7,"S",120,5,"adbd",20305
+6824713437605000,5,30000,6824713437635000,2728,"S",120,739,"shell",20461
+6824713437630000,6,82000,6824713437712000,739,"S",120,739,"adbd",20305
 6824713437635000,5,3002000,6824713440637000,0,"R",120,"[NULL]","swapper/5",0
 6824713437712000,6,15000,6824713437727000,0,"R",120,"[NULL]","swapper/5",0
-6824713437727000,6,14000,6824713437741000,2,"S",100,2,"kworker/u17:1",1134
+6824713437727000,6,14000,6824713437741000,630,"S",100,630,"kworker/u17:1",1134
 6824713437741000,6,40000,6824713437781000,0,"R",120,"[NULL]","swapper/5",0
-6824713437781000,6,19000,6824713437800000,2,"S",100,2,"kworker/u17:1",1134
-6824713437800000,6,9000,6824713437809000,3,"S",120,3,"kworker/6:1",14833
+6824713437781000,6,19000,6824713437800000,630,"S",100,630,"kworker/u17:1",1134
+6824713437800000,6,9000,6824713437809000,669,"S",120,669,"kworker/6:1",14833
 6824713437809000,6,39000,6824713437848000,0,"R",120,"[NULL]","swapper/5",0
-6824713437848000,6,25000,6824713437873000,2,"S",100,2,"kworker/u17:1",1134
+6824713437848000,6,25000,6824713437873000,630,"S",100,630,"kworker/u17:1",1134
 6824713437873000,6,136000,6824713438009000,0,"R",120,"[NULL]","swapper/5",0
-6824713437982000,7,13000,6824713437995000,4,"S",120,5,"UsbFfs-worker",20308
+6824713437982000,7,13000,6824713437995000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713437995000,7,43000,6824713438038000,0,"R",120,"[NULL]","swapper/5",0
-6824713438009000,6,19000,6824713438028000,2,"S",100,2,"kworker/u17:1",1134
-6824713438028000,6,10000,6824713438038000,3,"S",120,3,"kworker/6:1",14833
+6824713438009000,6,19000,6824713438028000,630,"S",100,630,"kworker/u17:1",1134
+6824713438028000,6,10000,6824713438038000,669,"S",120,669,"kworker/6:1",14833
+6824713438038000,7,7000,6824713438045000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713438038000,6,283000,6824713438321000,0,"R",120,"[NULL]","swapper/5",0
-6824713438038000,7,7000,6824713438045000,4,"S",120,5,"UsbFfs-worker",20308
 6824713438045000,7,470000,6824713438515000,0,"R",120,"[NULL]","swapper/5",0
-6824713438321000,6,20000,6824713438341000,2,"S",100,2,"kworker/u17:1",1134
+6824713438321000,6,20000,6824713438341000,630,"S",100,630,"kworker/u17:1",1134
 6824713438341000,6,34000,6824713438375000,0,"R",120,"[NULL]","swapper/5",0
-6824713438375000,6,29000,6824713438404000,2,"S",100,2,"kworker/u17:1",1134
+6824713438375000,6,29000,6824713438404000,630,"S",100,630,"kworker/u17:1",1134
 6824713438404000,6,71000,6824713438475000,0,"R",120,"[NULL]","swapper/5",0
-6824713438475000,6,29000,6824713438504000,2,"S",100,2,"kworker/u17:1",1134
-6824713438504000,6,14000,6824713438518000,3,"S",120,3,"kworker/6:1",14833
-6824713438515000,7,34000,6824713438549000,4,"S",120,5,"UsbFfs-worker",20308
+6824713438475000,6,29000,6824713438504000,630,"S",100,630,"kworker/u17:1",1134
+6824713438504000,6,14000,6824713438518000,669,"S",120,669,"kworker/6:1",14833
+6824713438515000,7,34000,6824713438549000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713438518000,6,10000,6824713438528000,0,"R",120,"[NULL]","swapper/5",0
-6824713438528000,6,43000,6824713438571000,7,"S",120,5,"adbd",20305
+6824713438528000,6,43000,6824713438571000,739,"S",120,739,"adbd",20305
 6824713438549000,7,3192000,6824713441741000,0,"R",120,"[NULL]","swapper/5",0
 6824713438571000,6,2294000,6824713440865000,0,"R",120,"[NULL]","swapper/5",0
-6824713440637000,5,30000,6824713440667000,82,"S",120,5,"shell",20461
+6824713440637000,5,30000,6824713440667000,2728,"S",120,739,"shell",20461
 6824713440667000,5,2936000,6824713443603000,0,"R",120,"[NULL]","swapper/5",0
-6824713440865000,6,84000,6824713440949000,7,"S",120,5,"adbd",20305
+6824713440865000,6,84000,6824713440949000,739,"S",120,739,"adbd",20305
 6824713440949000,6,533000,6824713441482000,0,"R",120,"[NULL]","swapper/5",0
-6824713441482000,6,36000,6824713441518000,2,"S",100,2,"kworker/u17:1",1134
-6824713441518000,6,12000,6824713441530000,3,"S",120,3,"kworker/6:1",14833
+6824713441482000,6,36000,6824713441518000,630,"S",100,630,"kworker/u17:1",1134
+6824713441518000,6,12000,6824713441530000,669,"S",120,669,"kworker/6:1",14833
 6824713441530000,6,315000,6824713441845000,0,"R",120,"[NULL]","swapper/5",0
-6824713441741000,7,12000,6824713441753000,4,"S",120,5,"UsbFfs-worker",20308
+6824713441741000,7,12000,6824713441753000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713441753000,7,349000,6824713442102000,0,"R",120,"[NULL]","swapper/5",0
-6824713441845000,6,22000,6824713441867000,2,"S",100,2,"kworker/u17:1",1134
-6824713441867000,6,11000,6824713441878000,3,"S",120,3,"kworker/6:1",14833
+6824713441845000,6,22000,6824713441867000,630,"S",100,630,"kworker/u17:1",1134
+6824713441867000,6,11000,6824713441878000,669,"S",120,669,"kworker/6:1",14833
 6824713441878000,6,270000,6824713442148000,0,"R",120,"[NULL]","swapper/5",0
-6824713442102000,7,15000,6824713442117000,4,"S",120,5,"UsbFfs-worker",20308
+6824713442102000,7,15000,6824713442117000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713442117000,7,369000,6824713442486000,0,"R",120,"[NULL]","swapper/5",0
-6824713442148000,6,16000,6824713442164000,2,"S",100,2,"kworker/u17:1",1134
+6824713442148000,6,16000,6824713442164000,630,"S",100,630,"kworker/u17:1",1134
 6824713442164000,6,35000,6824713442199000,0,"R",120,"[NULL]","swapper/5",0
-6824713442199000,6,26000,6824713442225000,2,"S",100,2,"kworker/u17:1",1134
+6824713442199000,6,26000,6824713442225000,630,"S",100,630,"kworker/u17:1",1134
 6824713442225000,6,51000,6824713442276000,0,"R",120,"[NULL]","swapper/5",0
-6824713442276000,6,29000,6824713442305000,2,"S",100,2,"kworker/u17:1",1134
-6824713442305000,6,15000,6824713442320000,3,"S",120,3,"kworker/6:1",14833
+6824713442276000,6,29000,6824713442305000,630,"S",100,630,"kworker/u17:1",1134
+6824713442305000,6,15000,6824713442320000,669,"S",120,669,"kworker/6:1",14833
 6824713442320000,6,184000,6824713442504000,0,"R",120,"[NULL]","swapper/5",0
-6824713442486000,7,38000,6824713442524000,4,"S",120,5,"UsbFfs-worker",20308
-6824713442504000,6,45000,6824713442549000,7,"S",120,5,"adbd",20305
+6824713442486000,7,38000,6824713442524000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713442504000,6,45000,6824713442549000,739,"S",120,739,"adbd",20305
 6824713442524000,7,2361000,6824713444885000,0,"R",120,"[NULL]","swapper/5",0
 6824713442549000,6,1704000,6824713444253000,0,"R",120,"[NULL]","swapper/5",0
-6824713443361000,2,92000,6824713443453000,6,"S",120,4,"rcu_preempt",7
+6824713443361000,2,92000,6824713443453000,5,"S",120,5,"rcu_preempt",7
 6824713443453000,2,362000,6824713443815000,0,"R",120,"[NULL]","swapper/5",0
-6824713443603000,5,161000,6824713443764000,9,"S",120,6,"rcuop/4",44
+6824713443603000,5,161000,6824713443764000,38,"S",120,38,"rcuop/4",44
 6824713443764000,5,254000,6824713444018000,0,"R",120,"[NULL]","swapper/5",0
-6824713443815000,2,49000,6824713443864000,6,"S",120,4,"rcu_preempt",7
+6824713443815000,2,49000,6824713443864000,5,"S",120,5,"rcu_preempt",7
 6824713443864000,2,6152000,6824713450016000,0,"R",120,"[NULL]","swapper/5",0
-6824713444018000,5,29000,6824713444047000,82,"S",120,5,"shell",20461
+6824713444018000,5,29000,6824713444047000,2728,"S",120,739,"shell",20461
 6824713444047000,5,6341000,6824713450388000,0,"R",120,"[NULL]","swapper/5",0
-6824713444253000,6,82000,6824713444335000,7,"S",120,5,"adbd",20305
+6824713444253000,6,82000,6824713444335000,739,"S",120,739,"adbd",20305
 6824713444335000,6,323000,6824713444658000,0,"R",120,"[NULL]","swapper/5",0
-6824713444658000,6,36000,6824713444694000,2,"S",100,2,"kworker/u17:1",1134
-6824713444694000,6,11000,6824713444705000,3,"S",120,3,"kworker/6:1",14833
+6824713444658000,6,36000,6824713444694000,630,"S",100,630,"kworker/u17:1",1134
+6824713444694000,6,11000,6824713444705000,669,"S",120,669,"kworker/6:1",14833
 6824713444705000,6,416000,6824713445121000,0,"R",120,"[NULL]","swapper/5",0
-6824713444885000,7,14000,6824713444899000,4,"S",120,5,"UsbFfs-worker",20308
+6824713444885000,7,14000,6824713444899000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713444899000,7,439000,6824713445338000,0,"R",120,"[NULL]","swapper/5",0
-6824713445121000,6,35000,6824713445156000,2,"S",100,2,"kworker/u17:1",1134
-6824713445156000,6,9000,6824713445165000,3,"S",120,3,"kworker/6:1",14833
+6824713445121000,6,35000,6824713445156000,630,"S",100,630,"kworker/u17:1",1134
+6824713445156000,6,9000,6824713445165000,669,"S",120,669,"kworker/6:1",14833
 6824713445165000,6,50000,6824713445215000,0,"R",120,"[NULL]","swapper/5",0
-6824713445215000,6,27000,6824713445242000,2,"S",100,2,"kworker/u17:1",1134
-6824713445242000,6,9000,6824713445251000,3,"S",120,3,"kworker/6:1",14833
+6824713445215000,6,27000,6824713445242000,630,"S",100,630,"kworker/u17:1",1134
+6824713445242000,6,9000,6824713445251000,669,"S",120,669,"kworker/6:1",14833
 6824713445251000,6,106000,6824713445357000,0,"R",120,"[NULL]","swapper/5",0
-6824713445338000,7,42000,6824713445380000,4,"S",120,5,"UsbFfs-worker",20308
-6824713445357000,6,38000,6824713445395000,7,"S",120,5,"adbd",20305
+6824713445338000,7,42000,6824713445380000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713445357000,6,38000,6824713445395000,739,"S",120,739,"adbd",20305
 6824713445380000,7,2465000,6824713447845000,0,"R",120,"[NULL]","swapper/5",0
 6824713445395000,6,2679000,6824713448074000,0,"R",120,"[NULL]","swapper/5",0
-6824713447845000,7,24000,6824713447869000,82,"S",120,5,"shell",20461
+6824713447845000,7,24000,6824713447869000,2728,"S",120,739,"shell",20461
 6824713447869000,7,782000,6824713448651000,0,"R",120,"[NULL]","swapper/5",0
-6824713448074000,6,85000,6824713448159000,7,"S",120,5,"adbd",20305
+6824713448074000,6,85000,6824713448159000,739,"S",120,739,"adbd",20305
 6824713448159000,6,279000,6824713448438000,0,"R",120,"[NULL]","swapper/5",0
-6824713448438000,6,36000,6824713448474000,2,"S",100,2,"kworker/u17:1",1134
-6824713448474000,6,11000,6824713448485000,3,"S",120,3,"kworker/6:1",14833
+6824713448438000,6,36000,6824713448474000,630,"S",100,630,"kworker/u17:1",1134
+6824713448474000,6,11000,6824713448485000,669,"S",120,669,"kworker/6:1",14833
 6824713448485000,6,380000,6824713448865000,0,"R",120,"[NULL]","swapper/5",0
-6824713448651000,7,12000,6824713448663000,4,"S",120,5,"UsbFfs-worker",20308
+6824713448651000,7,12000,6824713448663000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713448663000,7,456000,6824713449119000,0,"R",120,"[NULL]","swapper/5",0
-6824713448865000,6,23000,6824713448888000,2,"S",100,2,"kworker/u17:1",1134
-6824713448888000,6,11000,6824713448899000,3,"S",120,3,"kworker/6:1",14833
+6824713448865000,6,23000,6824713448888000,630,"S",100,630,"kworker/u17:1",1134
+6824713448888000,6,11000,6824713448899000,669,"S",120,669,"kworker/6:1",14833
 6824713448899000,6,269000,6824713449168000,0,"R",120,"[NULL]","swapper/5",0
-6824713449119000,7,15000,6824713449134000,4,"S",120,5,"UsbFfs-worker",20308
+6824713449119000,7,15000,6824713449134000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713449134000,7,353000,6824713449487000,0,"R",120,"[NULL]","swapper/5",0
-6824713449168000,6,27000,6824713449195000,2,"S",100,2,"kworker/u17:1",1134
+6824713449168000,6,27000,6824713449195000,630,"S",100,630,"kworker/u17:1",1134
 6824713449195000,6,48000,6824713449243000,0,"R",120,"[NULL]","swapper/5",0
-6824713449243000,6,29000,6824713449272000,2,"S",100,2,"kworker/u17:1",1134
-6824713449272000,6,15000,6824713449287000,3,"S",120,3,"kworker/6:1",14833
+6824713449243000,6,29000,6824713449272000,630,"S",100,630,"kworker/u17:1",1134
+6824713449272000,6,15000,6824713449287000,669,"S",120,669,"kworker/6:1",14833
 6824713449287000,6,389000,6824713449676000,0,"R",120,"[NULL]","swapper/5",0
-6824713449487000,7,38000,6824713449525000,4,"S",120,5,"UsbFfs-worker",20308
+6824713449487000,7,38000,6824713449525000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713449525000,7,2642000,6824713452167000,0,"R",120,"[NULL]","swapper/5",0
-6824713449676000,6,51000,6824713449727000,7,"S",120,5,"adbd",20305
+6824713449676000,6,51000,6824713449727000,739,"S",120,739,"adbd",20305
 6824713449727000,6,349000,6824713450076000,0,"R",120,"[NULL]","swapper/5",0
-6824713450016000,2,95000,6824713450111000,6,"S",120,4,"rcu_preempt",7
-6824713450076000,6,87000,6824713450163000,9,"S",120,6,"rcuop/4",44
+6824713450016000,2,95000,6824713450111000,5,"S",120,5,"rcu_preempt",7
+6824713450076000,6,87000,6824713450163000,38,"S",120,38,"rcuop/4",44
 6824713450111000,2,11329000,6824713461440000,0,"R",120,"[NULL]","swapper/5",0
 6824713450163000,6,2199000,6824713452362000,0,"R",120,"[NULL]","swapper/5",0
-6824713450388000,5,13000,6824713450401000,6,"S",120,4,"rcu_preempt",7
+6824713450388000,5,13000,6824713450401000,5,"S",120,5,"rcu_preempt",7
 6824713450401000,5,6481000,6824713456882000,0,"R",120,"[NULL]","swapper/5",0
-6824713452167000,7,26000,6824713452193000,82,"S",120,5,"shell",20461
+6824713452167000,7,26000,6824713452193000,2728,"S",120,739,"shell",20461
 6824713452193000,7,989000,6824713453182000,0,"R",120,"[NULL]","swapper/5",0
-6824713452362000,6,87000,6824713452449000,7,"S",120,5,"adbd",20305
+6824713452362000,6,87000,6824713452449000,739,"S",120,739,"adbd",20305
 6824713452449000,6,485000,6824713452934000,0,"R",120,"[NULL]","swapper/5",0
-6824713452934000,6,35000,6824713452969000,2,"S",100,2,"kworker/u17:1",1134
-6824713452969000,6,12000,6824713452981000,3,"S",120,3,"kworker/6:1",14833
+6824713452934000,6,35000,6824713452969000,630,"S",100,630,"kworker/u17:1",1134
+6824713452969000,6,12000,6824713452981000,669,"S",120,669,"kworker/6:1",14833
 6824713452981000,6,601000,6824713453582000,0,"R",120,"[NULL]","swapper/5",0
-6824713453182000,7,12000,6824713453194000,4,"S",120,5,"UsbFfs-worker",20308
+6824713453182000,7,12000,6824713453194000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713453194000,7,648000,6824713453842000,0,"R",120,"[NULL]","swapper/5",0
-6824713453582000,6,35000,6824713453617000,2,"S",100,2,"kworker/u17:1",1134
-6824713453617000,6,11000,6824713453628000,3,"S",120,3,"kworker/6:1",14833
+6824713453582000,6,35000,6824713453617000,630,"S",100,630,"kworker/u17:1",1134
+6824713453617000,6,11000,6824713453628000,669,"S",120,669,"kworker/6:1",14833
 6824713453628000,6,571000,6824713454199000,0,"R",120,"[NULL]","swapper/5",0
-6824713453842000,7,15000,6824713453857000,4,"S",120,5,"UsbFfs-worker",20308
+6824713453842000,7,15000,6824713453857000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713453857000,7,551000,6824713454408000,0,"R",120,"[NULL]","swapper/5",0
-6824713454199000,6,30000,6824713454229000,2,"S",100,2,"kworker/u17:1",1134
-6824713454229000,6,15000,6824713454244000,3,"S",120,3,"kworker/6:1",14833
+6824713454199000,6,30000,6824713454229000,630,"S",100,630,"kworker/u17:1",1134
+6824713454229000,6,15000,6824713454244000,669,"S",120,669,"kworker/6:1",14833
 6824713454244000,6,182000,6824713454426000,0,"R",120,"[NULL]","swapper/5",0
-6824713454408000,7,40000,6824713454448000,4,"S",120,5,"UsbFfs-worker",20308
-6824713454426000,6,45000,6824713454471000,7,"S",120,5,"adbd",20305
+6824713454408000,7,40000,6824713454448000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713454426000,6,45000,6824713454471000,739,"S",120,739,"adbd",20305
 6824713454448000,7,1495000,6824713455943000,0,"R",120,"[NULL]","swapper/5",0
 6824713454471000,6,1697000,6824713456168000,0,"R",120,"[NULL]","swapper/5",0
-6824713455943000,7,21000,6824713455964000,82,"S",120,5,"shell",20461
+6824713455943000,7,21000,6824713455964000,2728,"S",120,739,"shell",20461
 6824713455964000,7,1116000,6824713457080000,0,"R",120,"[NULL]","swapper/5",0
-6824713456168000,6,83000,6824713456251000,7,"S",120,5,"adbd",20305
+6824713456168000,6,83000,6824713456251000,739,"S",120,739,"adbd",20305
 6824713456251000,6,535000,6824713456786000,0,"R",120,"[NULL]","swapper/5",0
-6824713456565000,4,16000,6824713456581000,90,"S",120,74,"kworker/4:1",19875
-6824713456581000,4,3324000,6824713459905000,87,"R",120,71,"ps",20464
-6824713456786000,6,36000,6824713456822000,2,"S",100,2,"kworker/u17:1",1134
-6824713456822000,6,11000,6824713456833000,3,"S",120,3,"kworker/6:1",14833
+6824713456565000,4,16000,6824713456581000,716,"S",120,716,"kworker/4:1",19875
+6824713456581000,4,3324000,6824713459905000,2729,"R",120,758,"ps",20464
+6824713456786000,6,36000,6824713456822000,630,"S",100,630,"kworker/u17:1",1134
+6824713456822000,6,11000,6824713456833000,669,"S",120,669,"kworker/6:1",14833
 6824713456833000,6,411000,6824713457244000,0,"R",120,"[NULL]","swapper/5",0
-6824713456882000,5,15000,6824713456897000,6,"S",120,4,"rcu_preempt",7
+6824713456882000,5,15000,6824713456897000,5,"S",120,5,"rcu_preempt",7
 6824713456897000,5,644000,6824713457541000,0,"R",120,"[NULL]","swapper/5",0
-6824713457080000,7,12000,6824713457092000,4,"S",120,5,"UsbFfs-worker",20308
+6824713457080000,7,12000,6824713457092000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713457092000,7,408000,6824713457500000,0,"R",120,"[NULL]","swapper/5",0
-6824713457244000,6,70000,6824713457314000,9,"S",120,6,"rcuop/4",44
+6824713457244000,6,70000,6824713457314000,38,"S",120,38,"rcuop/4",44
 6824713457314000,6,585000,6824713457899000,0,"R",120,"[NULL]","swapper/5",0
-6824713457500000,7,44000,6824713457544000,2,"S",100,2,"kworker/u17:1",1134
-6824713457541000,5,8000,6824713457549000,6,"S",120,4,"rcu_preempt",7
-6824713457544000,7,12000,6824713457556000,30,"S",120,28,"kworker/7:2",14813
+6824713457500000,7,44000,6824713457544000,630,"S",100,630,"kworker/u17:1",1134
+6824713457541000,5,8000,6824713457549000,5,"S",120,5,"rcu_preempt",7
+6824713457544000,7,12000,6824713457556000,668,"S",120,668,"kworker/7:2",14813
 6824713457549000,5,2749000,6824713460298000,0,"R",120,"[NULL]","swapper/5",0
-6824713457556000,7,13000,6824713457569000,4,"S",120,5,"UsbFfs-worker",20308
+6824713457556000,7,13000,6824713457569000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713457569000,7,270000,6824713457839000,0,"R",120,"[NULL]","swapper/5",0
-6824713457839000,7,32000,6824713457871000,2,"S",100,2,"kworker/u17:1",1134
-6824713457871000,7,12000,6824713457883000,30,"S",120,28,"kworker/7:2",14813
-6824713457883000,7,36000,6824713457919000,4,"S",120,5,"UsbFfs-worker",20308
-6824713457899000,6,46000,6824713457945000,7,"S",120,5,"adbd",20305
+6824713457839000,7,32000,6824713457871000,630,"S",100,630,"kworker/u17:1",1134
+6824713457871000,7,12000,6824713457883000,668,"S",120,668,"kworker/7:2",14813
+6824713457883000,7,36000,6824713457919000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713457899000,6,46000,6824713457945000,739,"S",120,739,"adbd",20305
 6824713457919000,7,2183000,6824713460102000,0,"R",120,"[NULL]","swapper/5",0
 6824713457945000,6,5819000,6824713463764000,0,"R",120,"[NULL]","swapper/5",0
-6824713459905000,4,14000,6824713459919000,90,"S",120,74,"kworker/4:1",19875
-6824713459919000,4,6161000,6824713466080000,87,"x",120,71,"ps",20464
-6824713460102000,7,30000,6824713460132000,82,"S",120,5,"shell",20461
+6824713459905000,4,14000,6824713459919000,716,"S",120,716,"kworker/4:1",19875
+6824713459919000,4,6161000,6824713466080000,2729,"x",120,758,"ps",20464
+6824713460102000,7,30000,6824713460132000,2728,"S",120,739,"shell",20461
 6824713460132000,7,539000,6824713460671000,0,"R",120,"[NULL]","swapper/5",0
-6824713460298000,5,97000,6824713460395000,7,"S",120,5,"adbd",20305
+6824713460298000,5,97000,6824713460395000,739,"S",120,739,"adbd",20305
 6824713460395000,5,1447000,6824713461842000,0,"R",120,"[NULL]","swapper/5",0
-6824713460671000,7,35000,6824713460706000,2,"S",100,2,"kworker/u17:1",1134
-6824713460706000,7,8000,6824713460714000,30,"S",120,28,"kworker/7:2",14813
-6824713460714000,7,9000,6824713460723000,4,"S",120,5,"UsbFfs-worker",20308
+6824713460671000,7,35000,6824713460706000,630,"S",100,630,"kworker/u17:1",1134
+6824713460706000,7,8000,6824713460714000,668,"S",120,668,"kworker/7:2",14813
+6824713460714000,7,9000,6824713460723000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713460723000,7,306000,6824713461029000,0,"R",120,"[NULL]","swapper/5",0
-6824713461029000,7,22000,6824713461051000,2,"S",100,2,"kworker/u17:1",1134
-6824713461051000,7,8000,6824713461059000,30,"S",120,28,"kworker/7:2",14813
-6824713461059000,7,11000,6824713461070000,4,"S",120,5,"UsbFfs-worker",20308
+6824713461029000,7,22000,6824713461051000,630,"S",100,630,"kworker/u17:1",1134
+6824713461051000,7,8000,6824713461059000,668,"S",120,668,"kworker/7:2",14813
+6824713461059000,7,11000,6824713461070000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713461070000,7,261000,6824713461331000,0,"R",120,"[NULL]","swapper/5",0
-6824713461331000,7,30000,6824713461361000,2,"S",100,2,"kworker/u17:1",1134
-6824713461340000,0,109000,6824713461449000,35,"S",120,33,"kworker/0:5",20371
+6824713461331000,7,30000,6824713461361000,630,"S",100,630,"kworker/u17:1",1134
+6824713461340000,0,109000,6824713461449000,743,"S",120,743,"kworker/0:5",20371
 6824713461361000,7,250000,6824713461611000,0,"R",120,"[NULL]","swapper/5",0
-6824713461440000,2,373000,6824713461813000,36,"S",111,34,"SDM_EventThread",685
+6824713461440000,2,373000,6824713461813000,786,"S",111,494,"SDM_EventThread",685
 6824713461449000,0,460000,6824713461909000,0,"R",120,"[NULL]","swapper/5",0
-6824713461611000,7,30000,6824713461641000,2,"S",100,2,"kworker/u17:1",1134
-6824713461641000,7,13000,6824713461654000,30,"S",120,28,"kworker/7:2",14813
-6824713461654000,7,36000,6824713461690000,4,"S",120,5,"UsbFfs-worker",20308
+6824713461611000,7,30000,6824713461641000,630,"S",100,630,"kworker/u17:1",1134
+6824713461641000,7,13000,6824713461654000,668,"S",120,668,"kworker/7:2",14813
+6824713461654000,7,36000,6824713461690000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713461690000,7,2930000,6824713464620000,0,"R",120,"[NULL]","swapper/5",0
-6824713461708000,1,421000,6824713462129000,38,"S",120,35,"HwBinder:640_1",721
+6824713461708000,1,421000,6824713462129000,777,"S",120,493,"HwBinder:640_1",721
 6824713461813000,2,364000,6824713462177000,0,"R",120,"[NULL]","swapper/5",0
-6824713461842000,5,49000,6824713461891000,7,"S",120,5,"adbd",20305
+6824713461842000,5,49000,6824713461891000,739,"S",120,739,"adbd",20305
 6824713461891000,5,1341000,6824713463232000,0,"R",120,"[NULL]","swapper/5",0
-6824713461909000,0,68000,6824713461977000,40,"S",97,35,"DispSync",676
+6824713461909000,0,68000,6824713461977000,771,"S",97,493,"DispSync",676
 6824713461977000,0,38000,6824713462015000,0,"R",120,"[NULL]","swapper/5",0
-6824713462015000,0,162000,6824713462177000,40,"S",97,35,"DispSync",676
+6824713462015000,0,162000,6824713462177000,771,"S",97,493,"DispSync",676
 6824713462129000,1,262000,6824713462391000,0,"R",120,"[NULL]","swapper/5",0
+6824713462177000,2,255000,6824713462432000,773,"S",97,493,"app",678
 6824713462177000,0,142000,6824713462319000,0,"R",120,"[NULL]","swapper/5",0
-6824713462177000,2,255000,6824713462432000,41,"S",97,35,"app",678
-6824713462319000,0,1790000,6824713464109000,42,"S",120,36,"ndroid.systemui",1664
-6824713462391000,1,75000,6824713462466000,40,"S",97,35,"DispSync",676
+6824713462319000,0,1790000,6824713464109000,644,"S",120,644,"ndroid.systemui",1664
+6824713462391000,1,75000,6824713462466000,771,"S",97,493,"DispSync",676
 6824713462432000,2,1186000,6824713463618000,0,"R",120,"[NULL]","swapper/5",0
 6824713462466000,1,2550000,6824713465016000,0,"R",120,"[NULL]","swapper/5",0
-6824713463232000,5,9000,6824713463241000,6,"S",120,4,"rcu_preempt",7
+6824713463232000,5,9000,6824713463241000,5,"S",120,5,"rcu_preempt",7
 6824713463241000,5,334000,6824713463575000,0,"R",120,"[NULL]","swapper/5",0
-6824713463575000,5,14000,6824713463589000,6,"S",120,4,"rcu_preempt",7
+6824713463575000,5,14000,6824713463589000,5,"S",120,5,"rcu_preempt",7
 6824713463589000,5,243000,6824713463832000,0,"R",120,"[NULL]","swapper/5",0
-6824713463618000,2,521000,6824713464139000,16,"S",120,14,"kworker/u16:7",19422
-6824713463764000,6,70000,6824713463834000,9,"S",120,6,"rcuop/4",44
-6824713463832000,5,5000,6824713463837000,6,"S",120,4,"rcu_preempt",7
+6824713463618000,2,521000,6824713464139000,702,"S",120,702,"kworker/u16:7",19422
+6824713463764000,6,70000,6824713463834000,38,"S",120,38,"rcuop/4",44
+6824713463832000,5,5000,6824713463837000,5,"S",120,5,"rcu_preempt",7
 6824713463834000,6,2237000,6824713466071000,0,"R",120,"[NULL]","swapper/5",0
 6824713463837000,5,1034000,6824713464871000,0,"R",120,"[NULL]","swapper/5",0
 6824713464109000,0,488000,6824713464597000,0,"R",120,"[NULL]","swapper/5",0
 6824713464139000,2,5608000,6824713469747000,0,"R",120,"[NULL]","swapper/5",0
-6824713464149000,3,282000,6824713464431000,43,"S",120,35,"Binder:640_2",675
+6824713464149000,3,282000,6824713464431000,770,"S",120,493,"Binder:640_2",675
 6824713464431000,3,3478000,6824713467909000,0,"R",120,"[NULL]","swapper/5",0
-6824713464597000,0,172000,6824713464769000,41,"S",97,35,"app",678
-6824713464620000,7,27000,6824713464647000,82,"S",120,5,"shell",20461
+6824713464597000,0,172000,6824713464769000,773,"S",97,493,"app",678
+6824713464620000,7,27000,6824713464647000,2728,"S",120,739,"shell",20461
 6824713464647000,7,582000,6824713465229000,0,"R",120,"[NULL]","swapper/5",0
 6824713464769000,0,2190000,6824713466959000,0,"R",120,"[NULL]","swapper/5",0
-6824713464871000,5,85000,6824713464956000,7,"S",120,5,"adbd",20305
+6824713464871000,5,85000,6824713464956000,739,"S",120,739,"adbd",20305
 6824713464956000,5,693000,6824713465649000,0,"R",120,"[NULL]","swapper/5",0
-6824713465016000,1,91000,6824713465107000,40,"S",97,35,"DispSync",676
+6824713465016000,1,91000,6824713465107000,771,"S",97,493,"DispSync",676
 6824713465107000,1,1435000,6824713466542000,0,"R",120,"[NULL]","swapper/5",0
-6824713465229000,7,21000,6824713465250000,2,"S",100,2,"kworker/u17:1",1134
-6824713465250000,7,9000,6824713465259000,30,"S",120,28,"kworker/7:2",14813
-6824713465259000,7,10000,6824713465269000,4,"S",120,5,"UsbFfs-worker",20308
+6824713465229000,7,21000,6824713465250000,630,"S",100,630,"kworker/u17:1",1134
+6824713465250000,7,9000,6824713465259000,668,"S",120,668,"kworker/7:2",14813
+6824713465259000,7,10000,6824713465269000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713465269000,7,284000,6824713465553000,0,"R",120,"[NULL]","swapper/5",0
-6824713465553000,7,29000,6824713465582000,2,"S",100,2,"kworker/u17:1",1134
+6824713465553000,7,29000,6824713465582000,630,"S",100,630,"kworker/u17:1",1134
 6824713465582000,7,689000,6824713466271000,0,"R",120,"[NULL]","swapper/5",0
-6824713465649000,5,20000,6824713465669000,82,"S",120,5,"shell",20461
+6824713465649000,5,20000,6824713465669000,2728,"S",120,739,"shell",20461
 6824713465669000,5,418000,6824713466087000,0,"R",120,"[NULL]","swapper/5",0
-6824713466071000,6,16000,6824713466087000,27,"S",120,25,"rcuos/4",45
+6824713466071000,6,16000,6824713466087000,39,"S",120,39,"rcuos/4",45
 6824713466080000,4,812000,6824713466892000,0,"R",120,"[NULL]","swapper/5",0
 6824713466087000,6,4893000,6824713470980000,0,"R",120,"[NULL]","swapper/5",0
-6824713466087000,5,8000,6824713466095000,5,"S",120,7,"rcu_sched",8
+6824713466087000,5,8000,6824713466095000,6,"S",120,6,"rcu_sched",8
 6824713466095000,5,2929000,6824713469024000,0,"R",120,"[NULL]","swapper/5",0
-6824713466271000,7,37000,6824713466308000,2,"S",100,2,"kworker/u17:1",1134
-6824713466308000,7,8000,6824713466316000,30,"S",120,28,"kworker/7:2",14813
-6824713466316000,7,14000,6824713466330000,4,"S",120,5,"UsbFfs-worker",20308
+6824713466271000,7,37000,6824713466308000,630,"S",100,630,"kworker/u17:1",1134
+6824713466308000,7,8000,6824713466316000,668,"S",120,668,"kworker/7:2",14813
+6824713466316000,7,14000,6824713466330000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713466330000,7,308000,6824713466638000,0,"R",120,"[NULL]","swapper/5",0
-6824713466542000,1,3584000,6824713470126000,83,"x",120,67,"sh",20462
-6824713466638000,7,28000,6824713466666000,2,"S",100,2,"kworker/u17:1",1134
-6824713466666000,7,11000,6824713466677000,30,"S",120,28,"kworker/7:2",14813
-6824713466677000,7,39000,6824713466716000,4,"S",120,5,"UsbFfs-worker",20308
+6824713466542000,1,3584000,6824713470126000,2721,"x",120,756,"sh",20462
+6824713466638000,7,28000,6824713466666000,630,"S",100,630,"kworker/u17:1",1134
+6824713466666000,7,11000,6824713466677000,668,"S",120,668,"kworker/7:2",14813
+6824713466677000,7,39000,6824713466716000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713466716000,7,622000,6824713467338000,0,"R",120,"[NULL]","swapper/5",0
-6824713466892000,4,113000,6824713467005000,7,"S",120,5,"adbd",20305
-6824713466959000,0,61000,6824713467020000,11,"S",120,9,"rcuop/0",10
+6824713466892000,4,113000,6824713467005000,739,"S",120,739,"adbd",20305
+6824713466959000,0,61000,6824713467020000,8,"S",120,8,"rcuop/0",10
 6824713467005000,4,1148000,6824713468153000,0,"R",120,"[NULL]","swapper/5",0
 6824713467020000,0,1748000,6824713468768000,0,"R",120,"[NULL]","swapper/5",0
-6824713467338000,7,249000,6824713467587000,22,"D",49,20,"sugov:4",606
-6824713467587000,7,70000,6824713467657000,2,"S",100,2,"kworker/u17:1",1134
-6824713467657000,7,50000,6824713467707000,30,"S",120,28,"kworker/7:2",14813
-6824713467707000,7,60000,6824713467767000,4,"S",120,5,"UsbFfs-worker",20308
+6824713467338000,7,249000,6824713467587000,483,"D",49,483,"sugov:4",606
+6824713467587000,7,70000,6824713467657000,630,"S",100,630,"kworker/u17:1",1134
+6824713467657000,7,50000,6824713467707000,668,"S",120,668,"kworker/7:2",14813
+6824713467707000,7,60000,6824713467767000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713467767000,7,9375000,6824713477142000,0,"R",120,"[NULL]","swapper/5",0
-6824713467909000,3,104000,6824713468013000,32,"S",120,30,"smem_native_rpm",87
+6824713467909000,3,104000,6824713468013000,77,"S",120,77,"smem_native_rpm",87
 6824713468013000,3,1027000,6824713469040000,0,"R",120,"[NULL]","swapper/5",0
-6824713468153000,4,109000,6824713468262000,2,"R+",100,2,"kworker/u17:1",1134
-6824713468262000,4,82000,6824713468344000,22,"S",49,20,"sugov:4",606
-6824713468344000,4,36000,6824713468380000,90,"R",120,74,"kworker/4:1",19875
-6824713468380000,4,232000,6824713468612000,22,"D",49,20,"sugov:4",606
-6824713468612000,4,48000,6824713468660000,2,"S",100,2,"kworker/u17:1",1134
-6824713468660000,4,23000,6824713468683000,90,"S",120,74,"kworker/4:1",19875
+6824713468153000,4,109000,6824713468262000,630,"R+",100,630,"kworker/u17:1",1134
+6824713468262000,4,82000,6824713468344000,483,"S",49,483,"sugov:4",606
+6824713468344000,4,36000,6824713468380000,716,"R",120,716,"kworker/4:1",19875
+6824713468380000,4,232000,6824713468612000,483,"D",49,483,"sugov:4",606
+6824713468612000,4,48000,6824713468660000,630,"S",100,630,"kworker/u17:1",1134
+6824713468660000,4,23000,6824713468683000,716,"S",120,716,"kworker/4:1",19875
 6824713468683000,4,29000,6824713468712000,0,"R",120,"[NULL]","swapper/5",0
-6824713468712000,4,53000,6824713468765000,2,"S",100,2,"kworker/u17:1",1134
-6824713468765000,4,18000,6824713468783000,90,"S",120,74,"kworker/4:1",19875
-6824713468768000,0,71000,6824713468839000,79,"S",100,64,"kworker/u17:2",14944
+6824713468712000,4,53000,6824713468765000,630,"S",100,630,"kworker/u17:1",1134
+6824713468765000,4,18000,6824713468783000,716,"S",120,716,"kworker/4:1",19875
+6824713468768000,0,71000,6824713468839000,670,"S",100,670,"kworker/u17:2",14944
 6824713468783000,4,263000,6824713469046000,0,"R",120,"[NULL]","swapper/5",0
 6824713468839000,0,3202000,6824713472041000,0,"R",120,"[NULL]","swapper/5",0
-6824713469024000,5,45000,6824713469069000,4,"S",120,5,"UsbFfs-worker",20308
-6824713469040000,3,71000,6824713469111000,32,"S",120,30,"smem_native_rpm",87
-6824713469046000,4,43000,6824713469089000,7,"S",120,5,"adbd",20305
+6824713469024000,5,45000,6824713469069000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713469040000,3,71000,6824713469111000,77,"S",120,77,"smem_native_rpm",87
+6824713469046000,4,43000,6824713469089000,739,"S",120,739,"adbd",20305
 6824713469069000,5,850000,6824713469919000,0,"R",120,"[NULL]","swapper/5",0
 6824713469089000,4,153000,6824713469242000,0,"R",120,"[NULL]","swapper/5",0
 6824713469111000,3,1771000,6824713470882000,0,"R",120,"[NULL]","swapper/5",0
-6824713469242000,4,14000,6824713469256000,22,"S",49,20,"sugov:4",606
+6824713469242000,4,14000,6824713469256000,483,"S",49,483,"sugov:4",606
 6824713469256000,4,2174000,6824713471430000,0,"R",120,"[NULL]","swapper/5",0
-6824713469747000,2,68000,6824713469815000,14,"S",120,10,"rcuos/0",11
+6824713469747000,2,68000,6824713469815000,9,"S",120,9,"rcuos/0",11
 6824713469815000,2,281000,6824713470096000,0,"R",120,"[NULL]","swapper/5",0
-6824713469919000,5,7000,6824713469926000,5,"S",120,7,"rcu_sched",8
-6824713469926000,5,6000,6824713469932000,6,"S",120,4,"rcu_preempt",7
+6824713469919000,5,7000,6824713469926000,6,"S",120,6,"rcu_sched",8
+6824713469926000,5,6000,6824713469932000,5,"S",120,5,"rcu_preempt",7
 6824713469932000,5,770000,6824713470702000,0,"R",120,"[NULL]","swapper/5",0
-6824713470096000,2,3770000,6824713473866000,81,"x",120,66,"sh",20460
+6824713470096000,2,3770000,6824713473866000,2720,"x",120,755,"sh",20460
 6824713470126000,1,2368000,6824713472494000,0,"R",120,"[NULL]","swapper/5",0
-6824713470702000,5,43000,6824713470745000,6,"S",120,4,"rcu_preempt",7
+6824713470702000,5,43000,6824713470745000,5,"S",120,5,"rcu_preempt",7
 6824713470745000,5,381000,6824713471126000,0,"R",120,"[NULL]","swapper/5",0
-6824713470882000,3,62000,6824713470944000,23,"S",120,21,"rcuop/2",28
+6824713470882000,3,62000,6824713470944000,24,"S",120,24,"rcuop/2",28
 6824713470944000,3,15328000,6824713486272000,0,"R",120,"[NULL]","swapper/5",0
-6824713470980000,6,109000,6824713471089000,9,"S",120,6,"rcuop/4",44
+6824713470980000,6,109000,6824713471089000,38,"S",120,38,"rcuop/4",44
 6824713471089000,6,2971000,6824713474060000,0,"R",120,"[NULL]","swapper/5",0
-6824713471126000,5,89000,6824713471215000,82,"S",120,5,"shell",20461
+6824713471126000,5,89000,6824713471215000,2728,"S",120,739,"shell",20461
 6824713471215000,5,687000,6824713471902000,0,"R",120,"[NULL]","swapper/5",0
-6824713471430000,4,246000,6824713471676000,7,"S",120,5,"adbd",20305
+6824713471430000,4,246000,6824713471676000,739,"S",120,739,"adbd",20305
 6824713471676000,4,2529000,6824713474205000,0,"R",120,"[NULL]","swapper/5",0
-6824713471902000,5,68000,6824713471970000,82,"S",120,5,"shell",20461
+6824713471902000,5,68000,6824713471970000,2728,"S",120,739,"shell",20461
 6824713471970000,5,263000,6824713472233000,0,"R",120,"[NULL]","swapper/5",0
-6824713472041000,0,131000,6824713472172000,79,"S",100,64,"kworker/u17:2",14944
-6824713472172000,0,119000,6824713472291000,35,"S",120,33,"kworker/0:5",20371
-6824713472233000,5,23000,6824713472256000,4,"S",120,5,"UsbFfs-worker",20308
+6824713472041000,0,131000,6824713472172000,670,"S",100,670,"kworker/u17:2",14944
+6824713472172000,0,119000,6824713472291000,743,"S",120,743,"kworker/0:5",20371
+6824713472233000,5,23000,6824713472256000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713472256000,5,537000,6824713472793000,0,"R",120,"[NULL]","swapper/5",0
 6824713472291000,0,508000,6824713472799000,0,"R",120,"[NULL]","swapper/5",0
-6824713472494000,1,96000,6824713472590000,79,"S",100,64,"kworker/u17:2",14944
+6824713472494000,1,96000,6824713472590000,670,"S",100,670,"kworker/u17:2",14944
 6824713472590000,1,47000,6824713472637000,0,"R",120,"[NULL]","swapper/5",0
-6824713472637000,1,100000,6824713472737000,79,"S",100,64,"kworker/u17:2",14944
-6824713472737000,1,105000,6824713472842000,48,"S",120,41,"kworker/1:1",18800
-6824713472793000,5,17000,6824713472810000,4,"S",120,5,"UsbFfs-worker",20308
-6824713472799000,0,61000,6824713472860000,79,"S",100,64,"kworker/u17:2",14944
+6824713472637000,1,100000,6824713472737000,670,"S",100,670,"kworker/u17:2",14944
+6824713472737000,1,105000,6824713472842000,693,"S",120,693,"kworker/1:1",18800
+6824713472793000,5,17000,6824713472810000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713472799000,0,61000,6824713472860000,670,"S",100,670,"kworker/u17:2",14944
 6824713472810000,5,299000,6824713473109000,0,"R",120,"[NULL]","swapper/5",0
 6824713472842000,1,2946000,6824713475788000,0,"R",120,"[NULL]","swapper/5",0
 6824713472860000,0,44000,6824713472904000,0,"R",120,"[NULL]","swapper/5",0
-6824713472904000,0,75000,6824713472979000,79,"S",100,64,"kworker/u17:2",14944
+6824713472904000,0,75000,6824713472979000,670,"S",100,670,"kworker/u17:2",14944
 6824713472979000,0,453000,6824713473432000,0,"R",120,"[NULL]","swapper/5",0
-6824713473109000,5,8000,6824713473117000,20,"S",120,18,"rcuos/2",29
+6824713473109000,5,8000,6824713473117000,25,"S",120,25,"rcuos/2",29
 6824713473117000,5,861000,6824713473978000,0,"R",120,"[NULL]","swapper/5",0
-6824713473432000,0,106000,6824713473538000,79,"S",100,64,"kworker/u17:2",14944
-6824713473538000,0,135000,6824713473673000,35,"S",120,33,"kworker/0:5",20371
+6824713473432000,0,106000,6824713473538000,670,"S",100,670,"kworker/u17:2",14944
+6824713473538000,0,135000,6824713473673000,743,"S",120,743,"kworker/0:5",20371
 6824713473673000,0,946000,6824713474619000,0,"R",120,"[NULL]","swapper/5",0
 6824713473866000,2,283000,6824713474149000,0,"R",120,"[NULL]","swapper/5",0
-6824713473978000,5,75000,6824713474053000,4,"S",120,5,"UsbFfs-worker",20308
+6824713473978000,5,75000,6824713474053000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713474053000,5,975000,6824713475028000,0,"R",120,"[NULL]","swapper/5",0
-6824713474060000,6,244000,6824713474304000,82,"x",120,5,"shell",20461
-6824713474149000,2,49000,6824713474198000,8,"S",120,8,"rcuop/6",60
+6824713474060000,6,244000,6824713474304000,2728,"x",120,739,"shell",20461
+6824713474149000,2,49000,6824713474198000,52,"S",120,52,"rcuop/6",60
 6824713474198000,2,596000,6824713474794000,0,"R",120,"[NULL]","swapper/5",0
-6824713474205000,4,149000,6824713474354000,7,"S",120,5,"adbd",20305
+6824713474205000,4,149000,6824713474354000,739,"S",120,739,"adbd",20305
 6824713474304000,6,2488000,6824713476792000,0,"R",120,"[NULL]","swapper/5",0
 6824713474354000,4,1066000,6824713475420000,0,"R",120,"[NULL]","swapper/5",0
-6824713474619000,0,108000,6824713474727000,79,"S",100,64,"kworker/u17:2",14944
-6824713474727000,0,110000,6824713474837000,35,"S",120,33,"kworker/0:5",20371
-6824713474794000,2,81000,6824713474875000,79,"S",100,64,"kworker/u17:2",14944
+6824713474619000,0,108000,6824713474727000,670,"S",100,670,"kworker/u17:2",14944
+6824713474727000,0,110000,6824713474837000,743,"S",120,743,"kworker/0:5",20371
+6824713474794000,2,81000,6824713474875000,670,"S",100,670,"kworker/u17:2",14944
 6824713474837000,0,521000,6824713475358000,0,"R",120,"[NULL]","swapper/5",0
 6824713474875000,2,189000,6824713475064000,0,"R",120,"[NULL]","swapper/5",0
-6824713475028000,5,13000,6824713475041000,4,"S",120,5,"UsbFfs-worker",20308
+6824713475028000,5,13000,6824713475041000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713475041000,5,348000,6824713475389000,0,"R",120,"[NULL]","swapper/5",0
-6824713475064000,2,217000,6824713475281000,79,"S",100,64,"kworker/u17:2",14944
-6824713475281000,2,288000,6824713475569000,31,"S",120,29,"kworker/2:0",18823
-6824713475358000,0,96000,6824713475454000,79,"S",100,64,"kworker/u17:2",14944
-6824713475389000,5,10000,6824713475399000,4,"S",120,5,"UsbFfs-worker",20308
+6824713475064000,2,217000,6824713475281000,670,"S",100,670,"kworker/u17:2",14944
+6824713475281000,2,288000,6824713475569000,694,"S",120,694,"kworker/2:0",18823
+6824713475358000,0,96000,6824713475454000,670,"S",100,670,"kworker/u17:2",14944
+6824713475389000,5,10000,6824713475399000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713475399000,5,31000,6824713475430000,0,"R",120,"[NULL]","swapper/5",0
-6824713475420000,4,11000,6824713475431000,2,"S",100,2,"kworker/u17:1",1134
-6824713475430000,5,7000,6824713475437000,4,"S",120,5,"UsbFfs-worker",20308
+6824713475420000,4,11000,6824713475431000,630,"S",100,630,"kworker/u17:1",1134
+6824713475430000,5,7000,6824713475437000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713475431000,4,340000,6824713475771000,0,"R",120,"[NULL]","swapper/5",0
 6824713475437000,5,75000,6824713475512000,0,"R",120,"[NULL]","swapper/5",0
-6824713475454000,0,114000,6824713475568000,35,"S",120,33,"kworker/0:5",20371
-6824713475512000,5,39000,6824713475551000,4,"S",120,5,"UsbFfs-worker",20308
+6824713475454000,0,114000,6824713475568000,743,"S",120,743,"kworker/0:5",20371
+6824713475512000,5,39000,6824713475551000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713475551000,5,1067000,6824713476618000,0,"R",120,"[NULL]","swapper/5",0
 6824713475568000,0,10190000,6824713485758000,0,"R",120,"[NULL]","swapper/5",0
 6824713475569000,2,10187000,6824713485756000,0,"R",120,"[NULL]","swapper/5",0
-6824713475771000,4,98000,6824713475869000,7,"S",120,5,"adbd",20305
-6824713475788000,1,89000,6824713475877000,79,"S",100,64,"kworker/u17:2",14944
+6824713475771000,4,98000,6824713475869000,739,"S",120,739,"adbd",20305
+6824713475788000,1,89000,6824713475877000,670,"S",100,670,"kworker/u17:2",14944
 6824713475869000,4,1395000,6824713477264000,0,"R",120,"[NULL]","swapper/5",0
 6824713475877000,1,621000,6824713476498000,0,"R",120,"[NULL]","swapper/5",0
-6824713476498000,1,200000,6824713476698000,79,"S",100,64,"kworker/u17:2",14944
-6824713476618000,5,8000,6824713476626000,6,"S",120,4,"rcu_preempt",7
-6824713476626000,5,41000,6824713476667000,16,"S",120,14,"kworker/u16:7",19422
+6824713476498000,1,200000,6824713476698000,670,"S",100,670,"kworker/u17:2",14944
+6824713476618000,5,8000,6824713476626000,5,"S",120,5,"rcu_preempt",7
+6824713476626000,5,41000,6824713476667000,702,"S",120,702,"kworker/u16:7",19422
 6824713476667000,5,361000,6824713477028000,0,"R",120,"[NULL]","swapper/5",0
-6824713476698000,1,154000,6824713476852000,48,"S",120,41,"kworker/1:1",18800
-6824713476792000,6,30000,6824713476822000,5,"S",120,7,"rcu_sched",8
+6824713476698000,1,154000,6824713476852000,693,"S",120,693,"kworker/1:1",18800
+6824713476792000,6,30000,6824713476822000,6,"S",120,6,"rcu_sched",8
 6824713476822000,6,7677000,6824713484499000,0,"R",120,"[NULL]","swapper/5",0
 6824713476852000,1,9339000,6824713486191000,0,"R",120,"[NULL]","swapper/5",0
-6824713477028000,5,42000,6824713477070000,4,"S",120,5,"UsbFfs-worker",20308
+6824713477028000,5,42000,6824713477070000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713477070000,5,7334000,6824713484404000,0,"R",120,"[NULL]","swapper/5",0
-6824713477142000,7,19000,6824713477161000,27,"S",120,25,"rcuos/4",45
+6824713477142000,7,19000,6824713477161000,39,"S",120,39,"rcuos/4",45
 6824713477161000,7,61280000,6824713538441000,0,"R",120,"[NULL]","swapper/5",0
-6824713477264000,4,40000,6824713477304000,7,"S",120,5,"adbd",20305
+6824713477264000,4,40000,6824713477304000,739,"S",120,739,"adbd",20305
 6824713477304000,4,31231000,6824713508535000,0,"R",120,"[NULL]","swapper/5",0
-6824713484404000,5,44000,6824713484448000,6,"S",120,4,"rcu_preempt",7
+6824713484404000,5,44000,6824713484448000,5,"S",120,5,"rcu_preempt",7
 6824713484448000,5,365000,6824713484813000,0,"R",120,"[NULL]","swapper/5",0
-6824713484499000,6,23000,6824713484522000,5,"S",120,7,"rcu_sched",8
+6824713484499000,6,23000,6824713484522000,6,"S",120,6,"rcu_sched",8
 6824713484522000,6,6802000,6824713491324000,0,"R",120,"[NULL]","swapper/5",0
-6824713484813000,5,36000,6824713484849000,20,"S",120,18,"rcuos/2",29
+6824713484813000,5,36000,6824713484849000,25,"S",120,25,"rcuos/2",29
 6824713484849000,5,6226000,6824713491075000,0,"R",120,"[NULL]","swapper/5",0
-6824713485756000,2,159000,6824713485915000,14,"S",120,10,"rcuos/0",11
-6824713485758000,0,157000,6824713485915000,11,"S",120,9,"rcuop/0",10
+6824713485756000,2,159000,6824713485915000,9,"S",120,9,"rcuos/0",11
+6824713485758000,0,157000,6824713485915000,8,"S",120,8,"rcuop/0",10
 6824713485915000,2,6523000,6824713492438000,0,"R",120,"[NULL]","swapper/5",0
 6824713485915000,0,9244000,6824713495159000,0,"R",120,"[NULL]","swapper/5",0
-6824713486191000,1,100000,6824713486291000,13,"S",120,12,"rcuop/1",20
-6824713486272000,3,94000,6824713486366000,15,"S",120,13,"rcuos/1",21
+6824713486191000,1,100000,6824713486291000,17,"S",120,17,"rcuop/1",20
+6824713486272000,3,94000,6824713486366000,18,"S",120,18,"rcuos/1",21
 6824713486291000,1,9985000,6824713496276000,0,"R",120,"[NULL]","swapper/5",0
 6824713486366000,3,6074000,6824713492440000,0,"R",120,"[NULL]","swapper/5",0
-6824713491075000,5,58000,6824713491133000,6,"S",120,4,"rcu_preempt",7
+6824713491075000,5,58000,6824713491133000,5,"S",120,5,"rcu_preempt",7
 6824713491133000,5,6463000,6824713497596000,0,"R",120,"[NULL]","swapper/5",0
-6824713491324000,6,54000,6824713491378000,9,"S",120,6,"rcuop/4",44
+6824713491324000,6,54000,6824713491378000,38,"S",120,38,"rcuop/4",44
 6824713491378000,6,47434000,6824713538812000,0,"R",120,"[NULL]","swapper/5",0
-6824713492438000,2,218000,6824713492656000,8,"S",120,8,"rcuop/6",60
-6824713492440000,3,215000,6824713492655000,23,"S",120,21,"rcuop/2",28
+6824713492438000,2,218000,6824713492656000,52,"S",120,52,"rcuop/6",60
+6824713492440000,3,215000,6824713492655000,24,"S",120,24,"rcuop/2",28
 6824713492655000,3,6627000,6824713499282000,0,"R",120,"[NULL]","swapper/5",0
 6824713492656000,2,2845000,6824713495501000,0,"R",120,"[NULL]","swapper/5",0
-6824713495159000,0,143000,6824713495302000,35,"S",120,33,"kworker/0:5",20371
+6824713495159000,0,143000,6824713495302000,743,"S",120,743,"kworker/0:5",20371
 6824713495302000,0,61000,6824713495363000,0,"R",120,"[NULL]","swapper/5",0
-6824713495363000,0,62000,6824713495425000,44,"S",120,37,"ksoftirqd/0",3
+6824713495363000,0,62000,6824713495425000,3,"S",120,3,"ksoftirqd/0",3
 6824713495425000,0,586000,6824713496011000,0,"R",120,"[NULL]","swapper/5",0
-6824713495501000,2,575000,6824713496076000,36,"S",111,34,"SDM_EventThread",685
-6824713496011000,0,264000,6824713496275000,40,"S",97,35,"DispSync",676
+6824713495501000,2,575000,6824713496076000,786,"S",111,494,"SDM_EventThread",685
+6824713496011000,0,264000,6824713496275000,771,"S",97,493,"DispSync",676
 6824713496076000,2,458000,6824713496534000,0,"R",120,"[NULL]","swapper/5",0
 6824713496275000,0,505000,6824713496780000,0,"R",120,"[NULL]","swapper/5",0
-6824713496276000,1,516000,6824713496792000,38,"S",120,35,"HwBinder:640_1",721
-6824713496534000,2,458000,6824713496992000,41,"S",97,35,"app",678
-6824713496780000,0,101000,6824713496881000,40,"S",97,35,"DispSync",676
+6824713496276000,1,516000,6824713496792000,777,"S",120,493,"HwBinder:640_1",721
+6824713496534000,2,458000,6824713496992000,773,"S",97,493,"app",678
+6824713496780000,0,101000,6824713496881000,771,"S",97,493,"DispSync",676
 6824713496792000,1,472000,6824713497264000,0,"R",120,"[NULL]","swapper/5",0
 6824713496881000,0,83000,6824713496964000,0,"R",120,"[NULL]","swapper/5",0
-6824713496964000,0,65000,6824713497029000,40,"S",97,35,"DispSync",676
+6824713496964000,0,65000,6824713497029000,771,"S",97,493,"DispSync",676
 6824713496992000,2,10168000,6824713507160000,0,"R",120,"[NULL]","swapper/5",0
 6824713497029000,0,2730000,6824713499759000,0,"R",120,"[NULL]","swapper/5",0
-6824713497264000,1,2038000,6824713499302000,42,"S",120,36,"ndroid.systemui",1664
-6824713497596000,5,25000,6824713497621000,6,"S",120,4,"rcu_preempt",7
+6824713497264000,1,2038000,6824713499302000,644,"S",120,644,"ndroid.systemui",1664
+6824713497596000,5,25000,6824713497621000,5,"S",120,5,"rcu_preempt",7
 6824713497621000,5,6805000,6824713504426000,0,"R",120,"[NULL]","swapper/5",0
-6824713499282000,3,328000,6824713499610000,43,"S",120,35,"Binder:640_2",675
+6824713499282000,3,328000,6824713499610000,770,"S",120,493,"Binder:640_2",675
 6824713499302000,1,572000,6824713499874000,0,"R",120,"[NULL]","swapper/5",0
 6824713499610000,3,7250000,6824713506860000,0,"R",120,"[NULL]","swapper/5",0
-6824713499759000,0,267000,6824713500026000,41,"S",97,35,"app",678
-6824713499874000,1,154000,6824713500028000,40,"S",97,35,"DispSync",676
+6824713499759000,0,267000,6824713500026000,773,"S",97,493,"app",678
+6824713499874000,1,154000,6824713500028000,771,"S",97,493,"DispSync",676
 6824713500026000,0,6835000,6824713506861000,0,"R",120,"[NULL]","swapper/5",0
 6824713500028000,1,7524000,6824713507552000,0,"R",120,"[NULL]","swapper/5",0
-6824713504426000,5,314000,6824713504740000,22,"D",49,20,"sugov:4",606
-6824713504740000,5,82000,6824713504822000,6,"S",120,4,"rcu_preempt",7
+6824713504426000,5,314000,6824713504740000,483,"D",49,483,"sugov:4",606
+6824713504740000,5,82000,6824713504822000,5,"S",120,5,"rcu_preempt",7
 6824713504822000,5,26596000,6824713531418000,0,"R",120,"[NULL]","swapper/5",0
-6824713506860000,3,291000,6824713507151000,32,"S",120,30,"smem_native_rpm",87
-6824713506861000,0,290000,6824713507151000,11,"S",120,9,"rcuop/0",10
+6824713506860000,3,291000,6824713507151000,77,"S",120,77,"smem_native_rpm",87
+6824713506861000,0,290000,6824713507151000,8,"S",120,8,"rcuop/0",10
 6824713507151000,3,22498000,6824713529649000,0,"R",120,"[NULL]","swapper/5",0
 6824713507151000,0,1456000,6824713508607000,0,"R",120,"[NULL]","swapper/5",0
-6824713507160000,2,99000,6824713507259000,25,"S",120,24,"ksoftirqd/2",25
+6824713507160000,2,99000,6824713507259000,22,"S",120,22,"ksoftirqd/2",25
 6824713507259000,2,10658000,6824713517917000,0,"R",120,"[NULL]","swapper/5",0
-6824713507552000,1,436000,6824713507988000,13,"S",120,12,"rcuop/1",20
+6824713507552000,1,436000,6824713507988000,17,"S",120,17,"rcuop/1",20
 6824713507988000,1,16424000,6824713524412000,0,"R",120,"[NULL]","swapper/5",0
-6824713508535000,4,250000,6824713508785000,22,"S",49,20,"sugov:4",606
-6824713508607000,0,56000,6824713508663000,11,"S",120,9,"rcuop/0",10
+6824713508535000,4,250000,6824713508785000,483,"S",49,483,"sugov:4",606
+6824713508607000,0,56000,6824713508663000,8,"S",120,8,"rcuop/0",10
 6824713508663000,0,4135000,6824713512798000,0,"R",120,"[NULL]","swapper/5",0
 6824713508785000,4,35595000,6824713544380000,0,"R",120,"[NULL]","swapper/5",0
-6824713512798000,0,237000,6824713513035000,6,"S",120,4,"rcu_preempt",7
+6824713512798000,0,237000,6824713513035000,5,"S",120,5,"rcu_preempt",7
 6824713513035000,0,4264000,6824713517299000,0,"R",120,"[NULL]","swapper/5",0
-6824713517299000,0,410000,6824713517709000,6,"S",120,4,"rcu_preempt",7
-6824713517709000,0,229000,6824713517938000,23,"S",120,21,"rcuop/2",28
-6824713517917000,2,73000,6824713517990000,24,"S",120,22,"rcuop/3",36
-6824713517938000,0,144000,6824713518082000,9,"S",120,6,"rcuop/4",44
-6824713517990000,2,147000,6824713518137000,8,"S",120,8,"rcuop/6",60
+6824713517299000,0,410000,6824713517709000,5,"S",120,5,"rcu_preempt",7
+6824713517709000,0,229000,6824713517938000,24,"S",120,24,"rcuop/2",28
+6824713517917000,2,73000,6824713517990000,31,"S",120,31,"rcuop/3",36
+6824713517938000,0,144000,6824713518082000,38,"S",120,38,"rcuop/4",44
+6824713517990000,2,147000,6824713518137000,52,"S",120,52,"rcuop/6",60
 6824713518082000,0,5664000,6824713523746000,0,"R",120,"[NULL]","swapper/5",0
-6824713518137000,2,83000,6824713518220000,10,"S",120,11,"rcuop/5",52
+6824713518137000,2,83000,6824713518220000,45,"S",120,45,"rcuop/5",52
 6824713518220000,2,10550000,6824713528770000,0,"R",120,"[NULL]","swapper/5",0
-6824713523746000,0,88000,6824713523834000,6,"S",120,4,"rcu_preempt",7
-6824713523834000,0,154000,6824713523988000,11,"R+",120,9,"rcuop/0",10
-6824713523988000,0,28000,6824713524016000,6,"S",120,4,"rcu_preempt",7
-6824713524016000,0,43000,6824713524059000,11,"S",120,9,"rcuop/0",10
+6824713523746000,0,88000,6824713523834000,5,"S",120,5,"rcu_preempt",7
+6824713523834000,0,154000,6824713523988000,8,"R+",120,8,"rcuop/0",10
+6824713523988000,0,28000,6824713524016000,5,"S",120,5,"rcu_preempt",7
+6824713524016000,0,43000,6824713524059000,8,"S",120,8,"rcuop/0",10
 6824713524059000,0,4757000,6824713528816000,0,"R",120,"[NULL]","swapper/5",0
-6824713524412000,1,87000,6824713524499000,13,"S",120,12,"rcuop/1",20
+6824713524412000,1,87000,6824713524499000,17,"S",120,17,"rcuop/1",20
 6824713524499000,1,4727000,6824713529226000,0,"R",120,"[NULL]","swapper/5",0
-6824713528770000,2,179000,6824713528949000,25,"S",120,24,"ksoftirqd/2",25
-6824713528816000,0,175000,6824713528991000,35,"S",120,33,"kworker/0:5",20371
-6824713528949000,2,1466000,6824713530415000,36,"S",111,34,"SDM_EventThread",685
-6824713528991000,0,531000,6824713529522000,16,"R+",120,14,"kworker/u16:7",19422
-6824713529226000,1,134000,6824713529360000,79,"S",100,64,"kworker/u17:2",14944
+6824713528770000,2,179000,6824713528949000,22,"S",120,22,"ksoftirqd/2",25
+6824713528816000,0,175000,6824713528991000,743,"S",120,743,"kworker/0:5",20371
+6824713528949000,2,1466000,6824713530415000,786,"S",111,494,"SDM_EventThread",685
+6824713528991000,0,531000,6824713529522000,702,"R+",120,702,"kworker/u16:7",19422
+6824713529226000,1,134000,6824713529360000,670,"S",100,670,"kworker/u17:2",14944
 6824713529360000,1,961000,6824713530321000,0,"R",120,"[NULL]","swapper/5",0
-6824713529522000,0,135000,6824713529657000,79,"S",100,64,"kworker/u17:2",14944
-6824713529649000,3,475000,6824713530124000,40,"S",97,35,"DispSync",676
-6824713529657000,0,56000,6824713529713000,35,"R+",120,33,"kworker/0:5",20371
-6824713529713000,0,55000,6824713529768000,79,"S",100,64,"kworker/u17:2",14944
-6824713529768000,0,119000,6824713529887000,35,"R+",120,33,"kworker/0:5",20371
-6824713529887000,0,214000,6824713530101000,79,"S",100,64,"kworker/u17:2",14944
-6824713530101000,0,105000,6824713530206000,35,"S",120,33,"kworker/0:5",20371
-6824713530124000,3,93000,6824713530217000,32,"S",120,30,"smem_native_rpm",87
-6824713530206000,0,40000,6824713530246000,6,"S",120,4,"rcu_preempt",7
+6824713529522000,0,135000,6824713529657000,670,"S",100,670,"kworker/u17:2",14944
+6824713529649000,3,475000,6824713530124000,771,"S",97,493,"DispSync",676
+6824713529657000,0,56000,6824713529713000,743,"R+",120,743,"kworker/0:5",20371
+6824713529713000,0,55000,6824713529768000,670,"S",100,670,"kworker/u17:2",14944
+6824713529768000,0,119000,6824713529887000,743,"R+",120,743,"kworker/0:5",20371
+6824713529887000,0,214000,6824713530101000,670,"S",100,670,"kworker/u17:2",14944
+6824713530101000,0,105000,6824713530206000,743,"S",120,743,"kworker/0:5",20371
+6824713530124000,3,93000,6824713530217000,77,"S",120,77,"smem_native_rpm",87
+6824713530206000,0,40000,6824713530246000,5,"S",120,5,"rcu_preempt",7
 6824713530217000,3,33925000,6824713564142000,0,"R",120,"[NULL]","swapper/5",0
-6824713530246000,0,81000,6824713530327000,16,"S",120,14,"kworker/u16:7",19422
-6824713530321000,1,505000,6824713530826000,41,"S",97,35,"app",678
+6824713530246000,0,81000,6824713530327000,702,"S",120,702,"kworker/u16:7",19422
+6824713530321000,1,505000,6824713530826000,773,"S",97,493,"app",678
 6824713530327000,0,1013000,6824713531340000,0,"R",120,"[NULL]","swapper/5",0
 6824713530415000,2,1002000,6824713531417000,0,"R",120,"[NULL]","swapper/5",0
-6824713530826000,1,558000,6824713531384000,38,"S",120,35,"HwBinder:640_1",721
-6824713531340000,0,2759000,6824713534099000,42,"S",120,36,"ndroid.systemui",1664
+6824713530826000,1,558000,6824713531384000,777,"S",120,493,"HwBinder:640_1",721
+6824713531340000,0,2759000,6824713534099000,644,"S",120,644,"ndroid.systemui",1664
 6824713531384000,1,2683000,6824713534067000,0,"R",120,"[NULL]","swapper/5",0
-6824713531417000,2,89000,6824713531506000,40,"S",97,35,"DispSync",676
-6824713531418000,5,849000,6824713532267000,4,"S",120,5,"UsbFfs-worker",20308
+6824713531417000,2,89000,6824713531506000,771,"S",97,493,"DispSync",676
+6824713531418000,5,849000,6824713532267000,2487,"S",120,739,"UsbFfs-worker",20308
 6824713531506000,2,8373000,6824713539879000,0,"R",120,"[NULL]","swapper/5",0
-6824713532267000,5,3668000,6824713535935000,7,"S",120,5,"adbd",20305
-6824713534067000,1,310000,6824713534377000,43,"S",120,35,"Binder:640_2",675
+6824713532267000,5,3668000,6824713535935000,739,"S",120,739,"adbd",20305
+6824713534067000,1,310000,6824713534377000,770,"S",120,493,"Binder:640_2",675
 6824713534099000,0,433000,6824713534532000,0,"R",120,"[NULL]","swapper/5",0
 6824713534377000,1,510000,6824713534887000,0,"R",120,"[NULL]","swapper/5",0
-6824713534532000,0,188000,6824713534720000,41,"S",97,35,"app",678
+6824713534532000,0,188000,6824713534720000,773,"S",97,493,"app",678
 6824713534720000,0,2286000,6824713537006000,0,"R",120,"[NULL]","swapper/5",0
-6824713534887000,1,85000,6824713534972000,40,"S",97,35,"DispSync",676
+6824713534887000,1,85000,6824713534972000,771,"S",97,493,"DispSync",676
 6824713534972000,1,29038000,6824713564010000,0,"R",120,"[NULL]","swapper/5",0
-6824713535935000,5,3107000,6824713539042000,91,"R+",120,75,"sh",20465
-6824713537006000,0,74000,6824713537080000,6,"S",120,4,"rcu_preempt",7
-6824713537080000,0,59000,6824713537139000,11,"S",120,9,"rcuop/0",10
+6824713535935000,5,3107000,6824713539042000,2736,"R+",120,763,"sh",20465
+6824713537006000,0,74000,6824713537080000,5,"S",120,5,"rcu_preempt",7
+6824713537080000,0,59000,6824713537139000,8,"S",120,8,"rcuop/0",10
 6824713537139000,0,2739000,6824713539878000,0,"R",120,"[NULL]","swapper/5",0
-6824713538441000,7,113000,6824713538554000,27,"S",120,25,"rcuos/4",45
+6824713538441000,7,113000,6824713538554000,39,"S",120,39,"rcuos/4",45
 6824713538554000,7,5437000,6824713543991000,0,"R",120,"[NULL]","swapper/5",0
-6824713538812000,6,55000,6824713538867000,5,"S",120,7,"rcu_sched",8
+6824713538812000,6,55000,6824713538867000,6,"S",120,6,"rcu_sched",8
 6824713538867000,6,4782000,6824713543649000,0,"R",120,"[NULL]","swapper/5",0
-6824713539042000,5,1123000,6824713540165000,7,"S",120,5,"adbd",20305
-6824713539878000,0,278000,6824713540156000,9,"R+",120,6,"rcuop/4",44
-6824713539879000,2,332000,6824713540211000,76,"S",120,62,"hwrng",215
-6824713540156000,0,139000,6824713540295000,79,"S",100,64,"kworker/u17:2",14944
-6824713540165000,5,271000,6824713540436000,91,"R+",120,75,"sh",20465
+6824713539042000,5,1123000,6824713540165000,739,"S",120,739,"adbd",20305
+6824713539878000,0,278000,6824713540156000,38,"R+",120,38,"rcuop/4",44
+6824713539879000,2,332000,6824713540211000,145,"S",120,145,"hwrng",215
+6824713540156000,0,139000,6824713540295000,670,"S",100,670,"kworker/u17:2",14944
+6824713540165000,5,271000,6824713540436000,2736,"R+",120,763,"sh",20465
 6824713540211000,2,1202000,6824713541413000,0,"R",120,"[NULL]","swapper/5",0
-6824713540295000,0,37000,6824713540332000,6,"S",120,4,"rcu_preempt",7
-6824713540332000,0,118000,6824713540450000,35,"S",120,33,"kworker/0:5",20371
-6824713540436000,5,70000,6824713540506000,4,"S",120,5,"UsbFfs-worker",20308
-6824713540450000,0,46000,6824713540496000,9,"S",120,6,"rcuop/4",44
+6824713540295000,0,37000,6824713540332000,5,"S",120,5,"rcu_preempt",7
+6824713540332000,0,118000,6824713540450000,743,"S",120,743,"kworker/0:5",20371
+6824713540436000,5,70000,6824713540506000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713540450000,0,46000,6824713540496000,38,"S",120,38,"rcuop/4",44
 6824713540496000,0,316000,6824713540812000,0,"R",120,"[NULL]","swapper/5",0
-6824713540506000,5,886000,6824713541392000,91,"R",120,75,"sh",20465
-6824713540812000,0,76000,6824713540888000,79,"S",100,64,"kworker/u17:2",14944
+6824713540506000,5,886000,6824713541392000,2736,"R",120,763,"sh",20465
+6824713540812000,0,76000,6824713540888000,670,"S",100,670,"kworker/u17:2",14944
 6824713540888000,0,6183000,6824713547071000,0,"R",120,"[NULL]","swapper/5",0
-6824713541392000,5,115000,6824713541507000,79,"S",100,64,"kworker/u17:2",14944
-6824713541413000,2,46000,6824713541459000,76,"S",120,62,"hwrng",215
+6824713541392000,5,115000,6824713541507000,670,"S",100,670,"kworker/u17:2",14944
+6824713541413000,2,46000,6824713541459000,145,"S",120,145,"hwrng",215
 6824713541459000,2,6115000,6824713547574000,0,"R",120,"[NULL]","swapper/5",0
-6824713541507000,5,68000,6824713541575000,92,"R+",120,76,"kworker/5:2",19092
-6824713541575000,5,59000,6824713541634000,79,"S",100,64,"kworker/u17:2",14944
-6824713541634000,5,105000,6824713541739000,92,"S",120,76,"kworker/5:2",19092
-6824713541739000,5,246000,6824713541985000,4,"S",120,5,"UsbFfs-worker",20308
-6824713541985000,5,354000,6824713542339000,7,"R+",120,5,"adbd",20305
-6824713542339000,5,29000,6824713542368000,79,"S",100,64,"kworker/u17:2",14944
-6824713542368000,5,84000,6824713542452000,7,"S",120,5,"adbd",20305
-6824713542452000,5,329000,6824713542781000,91,"R+",120,75,"sh",20465
-6824713542781000,5,61000,6824713542842000,79,"S",100,64,"kworker/u17:2",14944
-6824713542842000,5,68000,6824713542910000,92,"S",120,76,"kworker/5:2",19092
-6824713542910000,5,36000,6824713542946000,4,"S",120,5,"UsbFfs-worker",20308
-6824713542946000,5,4105000,6824713547051000,91,"R",120,75,"sh",20465
-6824713543649000,6,83000,6824713543732000,5,"S",120,7,"rcu_sched",8
+6824713541507000,5,68000,6824713541575000,697,"R+",120,697,"kworker/5:2",19092
+6824713541575000,5,59000,6824713541634000,670,"S",100,670,"kworker/u17:2",14944
+6824713541634000,5,105000,6824713541739000,697,"S",120,697,"kworker/5:2",19092
+6824713541739000,5,246000,6824713541985000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713541985000,5,354000,6824713542339000,739,"R+",120,739,"adbd",20305
+6824713542339000,5,29000,6824713542368000,670,"S",100,670,"kworker/u17:2",14944
+6824713542368000,5,84000,6824713542452000,739,"S",120,739,"adbd",20305
+6824713542452000,5,329000,6824713542781000,2736,"R+",120,763,"sh",20465
+6824713542781000,5,61000,6824713542842000,670,"S",100,670,"kworker/u17:2",14944
+6824713542842000,5,68000,6824713542910000,697,"S",120,697,"kworker/5:2",19092
+6824713542910000,5,36000,6824713542946000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713542946000,5,4105000,6824713547051000,2736,"R",120,763,"sh",20465
+6824713543649000,6,83000,6824713543732000,6,"S",120,6,"rcu_sched",8
 6824713543732000,6,730000,6824713544462000,0,"R",120,"[NULL]","swapper/5",0
-6824713543991000,7,134000,6824713544125000,27,"S",120,25,"rcuos/4",45
+6824713543991000,7,134000,6824713544125000,39,"S",120,39,"rcuos/4",45
 6824713544125000,7,6499000,6824713550624000,0,"R",120,"[NULL]","swapper/5",0
-6824713544380000,4,97000,6824713544477000,93,"S",120,77,"rcuos/5",53
-6824713544462000,6,45000,6824713544507000,5,"S",120,7,"rcu_sched",8
+6824713544380000,4,97000,6824713544477000,46,"S",120,46,"rcuos/5",53
+6824713544462000,6,45000,6824713544507000,6,"S",120,6,"rcu_sched",8
 6824713544477000,4,6494000,6824713550971000,0,"R",120,"[NULL]","swapper/5",0
 6824713544507000,6,5784000,6824713550291000,0,"R",120,"[NULL]","swapper/5",0
-6824713547051000,5,95000,6824713547146000,6,"S",120,4,"rcu_preempt",7
-6824713547071000,0,137000,6824713547208000,62,"S",120,43,"Executor-7",14763
-6824713547146000,5,228000,6824713547374000,94,"R",120,5,"shell",20466
-6824713547208000,0,181000,6824713547389000,9,"S",120,6,"rcuop/4",44
-6824713547374000,5,21000,6824713547395000,6,"S",120,4,"rcu_preempt",7
+6824713547051000,5,95000,6824713547146000,5,"S",120,5,"rcu_preempt",7
+6824713547071000,0,137000,6824713547208000,1920,"S",120,667,"Executor-7",14763
+6824713547146000,5,228000,6824713547374000,2737,"R",120,739,"shell",20466
+6824713547208000,0,181000,6824713547389000,38,"S",120,38,"rcuop/4",44
+6824713547374000,5,21000,6824713547395000,5,"S",120,5,"rcu_preempt",7
 6824713547389000,0,13607000,6824713560996000,0,"R",120,"[NULL]","swapper/5",0
-6824713547395000,5,225000,6824713547620000,94,"S",120,5,"shell",20466
-6824713547574000,2,56000,6824713547630000,10,"S",120,11,"rcuop/5",52
-6824713547620000,5,5690000,6824713553310000,91,"R",120,75,"sh",20465
-6824713547630000,2,46000,6824713547676000,76,"S",120,62,"hwrng",215
+6824713547395000,5,225000,6824713547620000,2737,"S",120,739,"shell",20466
+6824713547574000,2,56000,6824713547630000,45,"S",120,45,"rcuop/5",52
+6824713547620000,5,5690000,6824713553310000,2736,"R",120,763,"sh",20465
+6824713547630000,2,46000,6824713547676000,145,"S",120,145,"hwrng",215
 6824713547676000,2,15043000,6824713562719000,0,"R",120,"[NULL]","swapper/5",0
-6824713550291000,6,80000,6824713550371000,5,"S",120,7,"rcu_sched",8
+6824713550291000,6,80000,6824713550371000,6,"S",120,6,"rcu_sched",8
 6824713550371000,6,24532000,6824713574903000,0,"R",120,"[NULL]","swapper/5",0
-6824713550624000,7,88000,6824713550712000,27,"S",120,25,"rcuos/4",45
+6824713550624000,7,88000,6824713550712000,39,"S",120,39,"rcuos/4",45
 6824713550712000,7,23645000,6824713574357000,0,"R",120,"[NULL]","swapper/5",0
-6824713550971000,4,48000,6824713551019000,93,"S",120,77,"rcuos/5",53
+6824713550971000,4,48000,6824713551019000,46,"S",120,46,"rcuos/5",53
 6824713551019000,4,14066000,6824713565085000,0,"R",120,"[NULL]","swapper/5",0
-6824713553310000,5,86000,6824713553396000,6,"S",120,4,"rcu_preempt",7
-6824713553396000,5,69000,6824713553465000,9,"S",120,6,"rcuop/4",44
-6824713553465000,5,36000,6824713553501000,10,"S",120,11,"rcuop/5",52
-6824713553501000,5,6486000,6824713559987000,91,"R",120,75,"sh",20465
-6824713559987000,5,57000,6824713560044000,92,"S",120,76,"kworker/5:2",19092
-6824713560044000,5,2577000,6824713562621000,91,"R+",120,75,"sh",20465
-6824713560996000,0,1098000,6824713562094000,95,"D",100,78,"FileWatcherThre",908
-6824713562094000,0,235000,6824713562329000,35,"S",120,33,"kworker/0:5",20371
+6824713553310000,5,86000,6824713553396000,5,"S",120,5,"rcu_preempt",7
+6824713553396000,5,69000,6824713553465000,38,"S",120,38,"rcuop/4",44
+6824713553465000,5,36000,6824713553501000,45,"S",120,45,"rcuop/5",52
+6824713553501000,5,6486000,6824713559987000,2736,"R",120,763,"sh",20465
+6824713559987000,5,57000,6824713560044000,697,"S",120,697,"kworker/5:2",19092
+6824713560044000,5,2577000,6824713562621000,2736,"R+",120,763,"sh",20465
+6824713560996000,0,1098000,6824713562094000,943,"D",100,563,"FileWatcherThre",908
+6824713562094000,0,235000,6824713562329000,743,"S",120,743,"kworker/0:5",20371
 6824713562329000,0,966000,6824713563295000,0,"R",120,"[NULL]","swapper/5",0
-6824713562621000,5,180000,6824713562801000,22,"S",49,20,"sugov:4",606
-6824713562719000,2,997000,6824713563716000,36,"S",111,34,"SDM_EventThread",685
-6824713562801000,5,33000,6824713562834000,9,"S",120,6,"rcuop/4",44
-6824713562834000,5,14000,6824713562848000,6,"S",120,4,"rcu_preempt",7
-6824713562848000,5,3776000,6824713566624000,91,"R",120,75,"sh",20465
-6824713563295000,0,346000,6824713563641000,40,"S",97,35,"DispSync",676
-6824713563641000,0,888000,6824713564529000,16,"S",120,14,"kworker/u16:7",19422
-6824713563716000,2,72000,6824713563788000,76,"S",120,62,"hwrng",215
+6824713562621000,5,180000,6824713562801000,483,"S",49,483,"sugov:4",606
+6824713562719000,2,997000,6824713563716000,786,"S",111,494,"SDM_EventThread",685
+6824713562801000,5,33000,6824713562834000,38,"S",120,38,"rcuop/4",44
+6824713562834000,5,14000,6824713562848000,5,"S",120,5,"rcu_preempt",7
+6824713562848000,5,3776000,6824713566624000,2736,"R",120,763,"sh",20465
+6824713563295000,0,346000,6824713563641000,771,"S",97,493,"DispSync",676
+6824713563641000,0,888000,6824713564529000,702,"S",120,702,"kworker/u16:7",19422
+6824713563716000,2,72000,6824713563788000,145,"S",120,145,"hwrng",215
 6824713563788000,2,972000,6824713564760000,0,"R",120,"[NULL]","swapper/5",0
-6824713564010000,1,574000,6824713564584000,38,"S",120,35,"HwBinder:640_1",721
-6824713564142000,3,453000,6824713564595000,41,"S",97,35,"app",678
-6824713564529000,0,91000,6824713564620000,35,"S",120,33,"kworker/0:5",20371
+6824713564010000,1,574000,6824713564584000,777,"S",120,493,"HwBinder:640_1",721
+6824713564142000,3,453000,6824713564595000,773,"S",97,493,"app",678
+6824713564529000,0,91000,6824713564620000,743,"S",120,743,"kworker/0:5",20371
 6824713564584000,1,2497000,6824713567081000,0,"R",120,"[NULL]","swapper/5",0
-6824713564595000,3,3222000,6824713567817000,42,"S",120,36,"ndroid.systemui",1664
+6824713564595000,3,3222000,6824713567817000,644,"S",120,644,"ndroid.systemui",1664
 6824713564620000,0,1978000,6824713566598000,0,"R",120,"[NULL]","swapper/5",0
-6824713564760000,2,90000,6824713564850000,40,"S",97,35,"DispSync",676
+6824713564760000,2,90000,6824713564850000,771,"S",97,493,"DispSync",676
 6824713564850000,2,3091000,6824713567941000,0,"R",120,"[NULL]","swapper/5",0
-6824713565085000,4,514000,6824713565599000,95,"D",100,78,"FileWatcherThre",908
+6824713565085000,4,514000,6824713565599000,943,"D",100,563,"FileWatcherThre",908
 6824713565599000,4,22373000,6824713587972000,0,"R",120,"[NULL]","swapper/5",0
-6824713566598000,0,117000,6824713566715000,35,"S",120,33,"kworker/0:5",20371
-6824713566624000,5,23000,6824713566647000,6,"S",120,4,"rcu_preempt",7
-6824713566647000,5,2458000,6824713569105000,91,"S",120,75,"sh",20465
+6824713566598000,0,117000,6824713566715000,743,"S",120,743,"kworker/0:5",20371
+6824713566624000,5,23000,6824713566647000,5,"S",120,5,"rcu_preempt",7
+6824713566647000,5,2458000,6824713569105000,2736,"S",120,763,"sh",20465
 6824713566715000,0,1121000,6824713567836000,0,"R",120,"[NULL]","swapper/5",0
-6824713567081000,1,530000,6824713567611000,95,"S",100,78,"FileWatcherThre",908
+6824713567081000,1,530000,6824713567611000,943,"S",100,563,"FileWatcherThre",908
 6824713567611000,1,1518000,6824713569129000,0,"R",120,"[NULL]","swapper/5",0
 6824713567817000,3,13943000,6824713581760000,0,"R",120,"[NULL]","swapper/5",0
-6824713567836000,0,68000,6824713567904000,11,"S",120,9,"rcuop/0",10
+6824713567836000,0,68000,6824713567904000,8,"S",120,8,"rcuop/0",10
 6824713567904000,0,700000,6824713568604000,0,"R",120,"[NULL]","swapper/5",0
-6824713567941000,2,390000,6824713568331000,43,"S",120,35,"Binder:640_2",675
+6824713567941000,2,390000,6824713568331000,770,"S",120,493,"Binder:640_2",675
 6824713568331000,2,4718000,6824713573049000,0,"R",120,"[NULL]","swapper/5",0
-6824713568604000,0,218000,6824713568822000,41,"S",97,35,"app",678
+6824713568604000,0,218000,6824713568822000,773,"S",97,493,"app",678
 6824713568822000,0,11756000,6824713580578000,0,"R",120,"[NULL]","swapper/5",0
-6824713569105000,5,4189000,6824713573294000,97,"R+",120,79,"atrace",20467
-6824713569129000,1,107000,6824713569236000,40,"S",97,35,"DispSync",676
+6824713569105000,5,4189000,6824713573294000,2738,"R+",120,764,"atrace",20467
+6824713569129000,1,107000,6824713569236000,771,"S",97,493,"DispSync",676
 6824713569236000,1,12634000,6824713581870000,0,"R",120,"[NULL]","swapper/5",0
-6824713573049000,2,113000,6824713573162000,76,"S",120,62,"hwrng",215
+6824713573049000,2,113000,6824713573162000,145,"S",120,145,"hwrng",215
 6824713573162000,2,2555000,6824713575717000,0,"R",120,"[NULL]","swapper/5",0
-6824713573294000,5,49000,6824713573343000,6,"S",120,4,"rcu_preempt",7
-6824713573343000,5,37000,6824713573380000,9,"S",120,6,"rcuop/4",44
-6824713573380000,5,23000,6824713573403000,10,"S",120,11,"rcuop/5",52
-6824713573403000,5,6607000,6824713580010000,97,"R+",120,79,"atrace",20467
-6824713574357000,7,104000,6824713574461000,27,"S",120,25,"rcuos/4",45
+6824713573294000,5,49000,6824713573343000,5,"S",120,5,"rcu_preempt",7
+6824713573343000,5,37000,6824713573380000,38,"S",120,38,"rcuop/4",44
+6824713573380000,5,23000,6824713573403000,45,"S",120,45,"rcuop/5",52
+6824713573403000,5,6607000,6824713580010000,2738,"R+",120,764,"atrace",20467
+6824713574357000,7,104000,6824713574461000,39,"S",120,39,"rcuos/4",45
 6824713574461000,7,12988000,6824713587449000,0,"R",120,"[NULL]","swapper/5",0
-6824713574903000,6,48000,6824713574951000,5,"S",120,7,"rcu_sched",8
+6824713574903000,6,48000,6824713574951000,6,"S",120,6,"rcu_sched",8
 6824713574951000,6,5495000,6824713580446000,0,"R",120,"[NULL]","swapper/5",0
-6824713575717000,2,111000,6824713575828000,76,"S",120,62,"hwrng",215
+6824713575717000,2,111000,6824713575828000,145,"S",120,145,"hwrng",215
 6824713575828000,2,22002000,6824713597830000,0,"R",120,"[NULL]","swapper/5",0
-6824713580010000,5,60000,6824713580070000,6,"S",120,4,"rcu_preempt",7
-6824713580070000,5,3250000,6824713583320000,97,"R+",120,79,"atrace",20467
-6824713580446000,6,32000,6824713580478000,5,"S",120,7,"rcu_sched",8
+6824713580010000,5,60000,6824713580070000,5,"S",120,5,"rcu_preempt",7
+6824713580070000,5,3250000,6824713583320000,2738,"R+",120,764,"atrace",20467
+6824713580446000,6,32000,6824713580478000,6,"S",120,6,"rcu_sched",8
 6824713580478000,6,6544000,6824713587022000,0,"R",120,"[NULL]","swapper/5",0
-6824713580578000,0,647000,6824713581225000,16,"D",120,14,"kworker/u16:7",19422
-6824713581225000,0,158000,6824713581383000,11,"S",120,9,"rcuop/0",10
+6824713580578000,0,647000,6824713581225000,702,"D",120,702,"kworker/u16:7",19422
+6824713581225000,0,158000,6824713581383000,8,"S",120,8,"rcuop/0",10
 6824713581383000,0,892000,6824713582275000,0,"R",120,"[NULL]","swapper/5",0
-6824713581760000,3,187000,6824713581947000,32,"S",120,30,"smem_native_rpm",87
-6824713581870000,1,91000,6824713581961000,13,"S",120,12,"rcuop/1",20
+6824713581760000,3,187000,6824713581947000,77,"S",120,77,"smem_native_rpm",87
+6824713581870000,1,91000,6824713581961000,17,"S",120,17,"rcuop/1",20
 6824713581947000,3,16557000,6824713598504000,0,"R",120,"[NULL]","swapper/5",0
 6824713581961000,1,15601000,6824713597562000,0,"R",120,"[NULL]","swapper/5",0
-6824713582275000,0,89000,6824713582364000,16,"S",120,14,"kworker/u16:7",19422
+6824713582275000,0,89000,6824713582364000,702,"S",120,702,"kworker/u16:7",19422
 6824713582364000,0,14442000,6824713596806000,0,"R",120,"[NULL]","swapper/5",0
-6824713583320000,5,148000,6824713583468000,22,"S",49,20,"sugov:4",606
-6824713583468000,5,3138000,6824713586606000,97,"R",120,79,"atrace",20467
-6824713586606000,5,29000,6824713586635000,6,"S",120,4,"rcu_preempt",7
-6824713586635000,5,43000,6824713586678000,9,"S",120,6,"rcuop/4",44
-6824713586678000,5,94000,6824713586772000,10,"S",120,11,"rcuop/5",52
-6824713586772000,5,8000,6824713586780000,6,"S",120,4,"rcu_preempt",7
-6824713586780000,5,6480000,6824713593260000,97,"R",120,79,"atrace",20467
-6824713587022000,6,46000,6824713587068000,5,"S",120,7,"rcu_sched",8
+6824713583320000,5,148000,6824713583468000,483,"S",49,483,"sugov:4",606
+6824713583468000,5,3138000,6824713586606000,2738,"R",120,764,"atrace",20467
+6824713586606000,5,29000,6824713586635000,5,"S",120,5,"rcu_preempt",7
+6824713586635000,5,43000,6824713586678000,38,"S",120,38,"rcuop/4",44
+6824713586678000,5,94000,6824713586772000,45,"S",120,45,"rcuop/5",52
+6824713586772000,5,8000,6824713586780000,5,"S",120,5,"rcu_preempt",7
+6824713586780000,5,6480000,6824713593260000,2738,"R",120,764,"atrace",20467
+6824713587022000,6,46000,6824713587068000,6,"S",120,6,"rcu_sched",8
 6824713587068000,6,1013000,6824713588081000,0,"R",120,"[NULL]","swapper/5",0
-6824713587449000,7,83000,6824713587532000,27,"S",120,25,"rcuos/4",45
+6824713587449000,7,83000,6824713587532000,39,"S",120,39,"rcuos/4",45
 6824713587532000,7,6610000,6824713594142000,0,"R",120,"[NULL]","swapper/5",0
-6824713587972000,4,51000,6824713588023000,93,"S",120,77,"rcuos/5",53
+6824713587972000,4,51000,6824713588023000,46,"S",120,46,"rcuos/5",53
 6824713588023000,4,6549000,6824713594572000,0,"R",120,"[NULL]","swapper/5",0
-6824713588081000,6,24000,6824713588105000,5,"S",120,7,"rcu_sched",8
+6824713588081000,6,24000,6824713588105000,6,"S",120,6,"rcu_sched",8
 6824713588105000,6,5555000,6824713593660000,0,"R",120,"[NULL]","swapper/5",0
-6824713593260000,5,24000,6824713593284000,6,"S",120,4,"rcu_preempt",7
-6824713593284000,5,18000,6824713593302000,9,"S",120,6,"rcuop/4",44
-6824713593302000,5,19000,6824713593321000,10,"S",120,11,"rcuop/5",52
-6824713593321000,5,9953000,6824713603274000,97,"R",120,79,"atrace",20467
-6824713593660000,6,42000,6824713593702000,5,"S",120,7,"rcu_sched",8
+6824713593260000,5,24000,6824713593284000,5,"S",120,5,"rcu_preempt",7
+6824713593284000,5,18000,6824713593302000,38,"S",120,38,"rcuop/4",44
+6824713593302000,5,19000,6824713593321000,45,"S",120,45,"rcuop/5",52
+6824713593321000,5,9953000,6824713603274000,2738,"R",120,764,"atrace",20467
+6824713593660000,6,42000,6824713593702000,6,"S",120,6,"rcu_sched",8
 6824713593702000,6,41271000,6824713634973000,0,"R",120,"[NULL]","swapper/5",0
-6824713594142000,7,54000,6824713594196000,27,"S",120,25,"rcuos/4",45
+6824713594142000,7,54000,6824713594196000,39,"S",120,39,"rcuos/4",45
 6824713594196000,7,40777000,6824713634973000,0,"R",120,"[NULL]","swapper/5",0
-6824713594572000,4,31000,6824713594603000,93,"S",120,77,"rcuos/5",53
+6824713594572000,4,31000,6824713594603000,46,"S",120,46,"rcuos/5",53
 6824713594603000,4,40370000,6824713634973000,0,"R",120,"[NULL]","swapper/5",0
-6824713596806000,0,434000,6824713597240000,40,"S",97,35,"DispSync",676
-6824713597240000,0,240000,6824713597480000,35,"S",120,33,"kworker/0:5",20371
+6824713596806000,0,434000,6824713597240000,771,"S",97,493,"DispSync",676
+6824713597240000,0,240000,6824713597480000,743,"S",120,743,"kworker/0:5",20371
 6824713597480000,0,864000,6824713598344000,0,"R",120,"[NULL]","swapper/5",0
-6824713597562000,1,518000,6824713598080000,41,"S",97,35,"app",678
-6824713597830000,2,805000,6824713598635000,36,"S",111,34,"SDM_EventThread",685
+6824713597562000,1,518000,6824713598080000,773,"S",97,493,"app",678
+6824713597830000,2,805000,6824713598635000,786,"S",111,494,"SDM_EventThread",685
 6824713598080000,1,765000,6824713598845000,0,"R",120,"[NULL]","swapper/5",0
-6824713598344000,0,3217000,6824713601561000,42,"S",120,36,"ndroid.systemui",1664
-6824713598504000,3,105000,6824713598609000,40,"S",97,35,"DispSync",676
+6824713598344000,0,3217000,6824713601561000,644,"S",120,644,"ndroid.systemui",1664
+6824713598504000,3,105000,6824713598609000,771,"S",97,493,"DispSync",676
 6824713598609000,3,32422000,6824713631031000,0,"R",120,"[NULL]","swapper/5",0
 6824713598635000,2,932000,6824713599567000,0,"R",120,"[NULL]","swapper/5",0
-6824713598845000,1,556000,6824713599401000,38,"S",120,35,"HwBinder:640_1",721
+6824713598845000,1,556000,6824713599401000,777,"S",120,493,"HwBinder:640_1",721
 6824713599401000,1,2173000,6824713601574000,0,"R",120,"[NULL]","swapper/5",0
-6824713599567000,2,90000,6824713599657000,40,"S",97,35,"DispSync",676
+6824713599567000,2,90000,6824713599657000,771,"S",97,493,"DispSync",676
 6824713599657000,2,23565000,6824713623222000,0,"R",120,"[NULL]","swapper/5",0
 6824713601561000,0,678000,6824713602239000,0,"R",120,"[NULL]","swapper/5",0
-6824713601574000,1,395000,6824713601969000,43,"S",120,35,"Binder:640_2",675
+6824713601574000,1,395000,6824713601969000,770,"S",120,493,"Binder:640_2",675
 6824713601969000,1,395000,6824713602364000,0,"R",120,"[NULL]","swapper/5",0
-6824713602239000,0,209000,6824713602448000,41,"S",97,35,"app",678
-6824713602364000,1,79000,6824713602443000,40,"S",97,35,"DispSync",676
+6824713602239000,0,209000,6824713602448000,773,"S",97,493,"app",678
+6824713602364000,1,79000,6824713602443000,771,"S",97,493,"DispSync",676
 6824713602443000,1,27933000,6824713630376000,0,"R",120,"[NULL]","swapper/5",0
 6824713602448000,0,26982000,6824713629430000,0,"R",120,"[NULL]","swapper/5",0
-6824713603274000,5,115000,6824713603389000,22,"S",49,20,"sugov:4",606
-6824713603389000,5,17861000,6824713621250000,97,"R+",120,79,"atrace",20467
-6824713621250000,5,138000,6824713621388000,22,"S",49,20,"sugov:4",606
-6824713621388000,5,35000,6824713621423000,9,"S",120,6,"rcuop/4",44
-6824713621423000,5,19000,6824713621442000,6,"S",120,4,"rcu_preempt",7
-6824713621442000,5,5171000,6824713626613000,97,"R",120,79,"atrace",20467
-6824713623222000,2,232000,6824713623454000,76,"S",120,62,"hwrng",215
+6824713603274000,5,115000,6824713603389000,483,"S",49,483,"sugov:4",606
+6824713603389000,5,17861000,6824713621250000,2738,"R+",120,764,"atrace",20467
+6824713621250000,5,138000,6824713621388000,483,"S",49,483,"sugov:4",606
+6824713621388000,5,35000,6824713621423000,38,"S",120,38,"rcuop/4",44
+6824713621423000,5,19000,6824713621442000,5,"S",120,5,"rcu_preempt",7
+6824713621442000,5,5171000,6824713626613000,2738,"R",120,764,"atrace",20467
+6824713623222000,2,232000,6824713623454000,145,"S",120,145,"hwrng",215
 6824713623454000,2,5577000,6824713629031000,0,"R",120,"[NULL]","swapper/5",0
-6824713626613000,5,45000,6824713626658000,6,"S",120,4,"rcu_preempt",7
-6824713626658000,5,26000,6824713626684000,9,"S",120,6,"rcuop/4",44
-6824713626684000,5,21000,6824713626705000,10,"S",120,11,"rcuop/5",52
-6824713626705000,5,6000,6824713626711000,6,"S",120,4,"rcu_preempt",7
-6824713626711000,5,282000,6824713626993000,97,"S",120,79,"atrace",20467
+6824713626613000,5,45000,6824713626658000,5,"S",120,5,"rcu_preempt",7
+6824713626658000,5,26000,6824713626684000,38,"S",120,38,"rcuop/4",44
+6824713626684000,5,21000,6824713626705000,45,"S",120,45,"rcuop/5",52
+6824713626705000,5,6000,6824713626711000,5,"S",120,5,"rcu_preempt",7
+6824713626711000,5,282000,6824713626993000,2738,"S",120,764,"atrace",20467
 6824713626993000,5,7817000,6824713634810000,0,"R",120,"[NULL]","swapper/5",0
-6824713629031000,2,2744000,6824713631775000,63,"R+",120,49,"hwservicemanage",602
-6824713629430000,0,258000,6824713629688000,35,"S",120,33,"kworker/0:5",20371
-6824713629688000,0,932000,6824713630620000,36,"R+",111,34,"SDM_EventThread",685
-6824713630376000,1,388000,6824713630764000,40,"S",97,35,"DispSync",676
-6824713630620000,0,466000,6824713631086000,41,"S",97,35,"app",678
+6824713629031000,2,2744000,6824713631775000,479,"R+",120,479,"hwservicemanage",602
+6824713629430000,0,258000,6824713629688000,743,"S",120,743,"kworker/0:5",20371
+6824713629688000,0,932000,6824713630620000,786,"R+",111,494,"SDM_EventThread",685
+6824713630376000,1,388000,6824713630764000,771,"S",97,493,"DispSync",676
+6824713630620000,0,466000,6824713631086000,773,"S",97,493,"app",678
 6824713630764000,1,886000,6824713631650000,0,"R",120,"[NULL]","swapper/5",0
-6824713631031000,3,505000,6824713631536000,38,"S",120,35,"HwBinder:640_1",721
-6824713631086000,0,116000,6824713631202000,16,"S",120,14,"kworker/u16:7",19422
-6824713631202000,0,181000,6824713631383000,36,"S",111,34,"SDM_EventThread",685
+6824713631031000,3,505000,6824713631536000,777,"S",120,493,"HwBinder:640_1",721
+6824713631086000,0,116000,6824713631202000,702,"S",120,702,"kworker/u16:7",19422
+6824713631202000,0,181000,6824713631383000,786,"S",111,494,"SDM_EventThread",685
 6824713631383000,0,2291000,6824713633674000,0,"R",120,"[NULL]","swapper/5",0
 6824713631536000,3,3437000,6824713634973000,0,"R",120,"[NULL]","swapper/5",0
-6824713631650000,1,63000,6824713631713000,40,"S",97,35,"DispSync",676
-6824713631713000,1,1737000,6824713633450000,42,"S",120,36,"ndroid.systemui",1664
-6824713631775000,2,255000,6824713632030000,26,"S",49,23,"sugov:0",605
-6824713632030000,2,108000,6824713632138000,63,"S",120,49,"hwservicemanage",602
-6824713632138000,2,437000,6824713632575000,97,"R+",120,79,"atrace",20467
-6824713632575000,2,334000,6824713632909000,63,"S",120,49,"hwservicemanage",602
-6824713632909000,2,263000,6824713633172000,97,"R+",120,79,"atrace",20467
-6824713633172000,2,340000,6824713633512000,98,"S",120,80,"atrace@1.0-serv",635
+6824713631650000,1,63000,6824713631713000,771,"S",97,493,"DispSync",676
+6824713631713000,1,1737000,6824713633450000,644,"S",120,644,"ndroid.systemui",1664
+6824713631775000,2,255000,6824713632030000,482,"S",49,482,"sugov:0",605
+6824713632030000,2,108000,6824713632138000,479,"S",120,479,"hwservicemanage",602
+6824713632138000,2,437000,6824713632575000,2738,"R+",120,764,"atrace",20467
+6824713632575000,2,334000,6824713632909000,479,"S",120,479,"hwservicemanage",602
+6824713632909000,2,263000,6824713633172000,2738,"R+",120,764,"atrace",20467
+6824713633172000,2,340000,6824713633512000,489,"S",120,489,"atrace@1.0-serv",635
 6824713633450000,1,1179000,6824713634629000,0,"R",120,"[NULL]","swapper/5",0
-6824713633512000,2,169000,6824713633681000,97,"R+",120,79,"atrace",20467
-6824713633674000,0,213000,6824713633887000,43,"S",120,35,"Binder:640_2",675
-6824713633681000,2,75000,6824713633756000,98,"S",120,80,"atrace@1.0-serv",635
-6824713633756000,2,1217000,6824713634973000,97,"R",120,79,"atrace",20467
+6824713633512000,2,169000,6824713633681000,2738,"R+",120,764,"atrace",20467
+6824713633674000,0,213000,6824713633887000,770,"S",120,493,"Binder:640_2",675
+6824713633681000,2,75000,6824713633756000,489,"S",120,489,"atrace@1.0-serv",635
+6824713633756000,2,1217000,6824713634973000,2738,"R",120,764,"atrace",20467
 6824713633887000,0,1086000,6824713634973000,0,"R",120,"[NULL]","swapper/5",0
-6824713634629000,1,131000,6824713634760000,41,"S",97,35,"app",678
+6824713634629000,1,131000,6824713634760000,773,"S",97,493,"app",678
 6824713634760000,1,213000,6824713634973000,0,"R",120,"[NULL]","swapper/5",0
-6824713634810000,5,70000,6824713634880000,6,"S",120,4,"rcu_preempt",7
-6824713634880000,5,39000,6824713634919000,9,"S",120,6,"rcuop/4",44
-6824713634919000,5,54000,6824713634973000,10,"S",120,11,"rcuop/5",52
+6824713634810000,5,70000,6824713634880000,5,"S",120,5,"rcu_preempt",7
+6824713634880000,5,39000,6824713634919000,38,"S",120,38,"rcuop/4",44
+6824713634919000,5,54000,6824713634973000,45,"S",120,45,"rcuop/5",52
 6824713634973000,5,0,6824713634973000,0,"R",120,"[NULL]","swapper/5",0
diff --git a/test/trace_processor/thread_time_in_state.out b/test/trace_processor/thread_time_in_state.out
new file mode 100644
index 0000000..7d88686
--- /dev/null
+++ b/test/trace_processor/thread_time_in_state.out
@@ -0,0 +1,8 @@
+"name","tid","ts","value"
+"cpu0.time_in_state",5,2,20.000000
+"cpu0.time_in_state",5,3,30.000000
+"cpu2.time_in_state",7,3,10.000000
+"cpu2.time_in_state",11,3,10.000000
+"cpu2.time_in_state",12,3,10.000000
+"cpu2.time_in_state",11,4,20.000000
+"cpu2.time_in_state",12,4,20.000000
diff --git a/test/trace_processor/thread_time_in_state.sql b/test/trace_processor/thread_time_in_state.sql
new file mode 100644
index 0000000..ac87c0f
--- /dev/null
+++ b/test/trace_processor/thread_time_in_state.sql
@@ -0,0 +1,9 @@
+SELECT
+t.name,
+tid,
+c.ts,
+c.value
+FROM counter c
+JOIN thread_counter_track t ON c.track_id = t.id
+JOIN thread USING (utid)
+ORDER BY c.ts;
diff --git a/test/trace_processor/thread_time_in_state.textproto b/test/trace_processor/thread_time_in_state.textproto
new file mode 100644
index 0000000..1d6c0f2
--- /dev/null
+++ b/test/trace_processor/thread_time_in_state.textproto
@@ -0,0 +1,92 @@
+packet {
+  system_info {
+    hz: 100
+  }
+}
+packet {
+  timestamp: 2
+  process_stats {
+    processes {
+      pid: 5
+      threads {
+        tid: 5
+        cpu_freq_indices: 1
+        cpu_freq_ticks: 1
+        cpu_freq_indices: 2
+        cpu_freq_ticks: 1
+      }
+    }
+  }
+}
+packet {
+  timestamp: 3
+  process_stats {
+    processes {
+      pid: 5
+      threads {
+        tid: 5
+        cpu_freq_indices: 1
+        cpu_freq_ticks: 2
+        cpu_freq_indices: 2
+        cpu_freq_ticks: 1
+      }
+      threads {
+        tid: 7
+        cpu_freq_indices: 5
+        cpu_freq_ticks: 1
+      }
+    }
+    processes {
+      pid: 11
+      threads {
+        tid: 11
+        cpu_freq_indices: 6
+        cpu_freq_ticks: 1
+      }
+      threads {
+        tid: 12
+        cpu_freq_indices: 6
+        cpu_freq_ticks: 1
+      }
+    }
+  }
+}
+packet {
+  timestamp: 4
+  process_stats {
+    processes {
+      pid: 11
+      threads {
+        tid: 11
+        cpu_freq_indices: 6
+        cpu_freq_ticks: 2
+      }
+      threads {
+        tid: 12
+        cpu_freq_indices: 6
+        cpu_freq_ticks: 2
+      }
+    }
+  }
+}
+packet {
+  timestamp: 1
+  cpu_info {
+    cpus {
+      frequencies: 100
+      frequencies: 200
+    }
+    cpus {
+      frequencies: 100
+      frequencies: 200
+    }
+    cpus {
+      frequencies: 1000
+      frequencies: 2000
+    }
+    cpus {
+      frequencies: 1000
+      frequencies: 2000
+    }
+  }
+}
diff --git a/test/trace_processor/track_event_counters.sql b/test/trace_processor/track_event_counters.sql
new file mode 100644
index 0000000..bfdce7e
--- /dev/null
+++ b/test/trace_processor/track_event_counters.sql
@@ -0,0 +1,32 @@
+--
+-- 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
+  counter_track.name as counter_name,
+  process.name as process,
+  thread.name as thread,
+  thread_process.name as thread_process,
+  counter_track.unit as unit,
+  counter_track.source_arg_set_id as track_args,
+  counter.ts,
+  counter.value
+from counter
+left join counter_track on counter.track_id = counter_track.id
+left join process_counter_track on counter.track_id = process_counter_track.id
+left join process on process_counter_track.upid = process.upid
+left join thread_counter_track on counter.track_id = thread_counter_track.id
+left join thread on thread_counter_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_counters.textproto b/test/trace_processor/track_event_counters.textproto
new file mode 100644
index 0000000..decdd68
--- /dev/null
+++ b/test/trace_processor/track_event_counters.textproto
@@ -0,0 +1,245 @@
+# Sequence 1 defaults to track for "t1" and extra_counter_values for "c1".
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 0
+  incremental_state_cleared: true
+  track_descriptor {
+    uuid: 1
+    parent_uuid: 3
+    thread {
+      pid: 5
+      tid: 1
+      thread_name: "t1"
+    }
+  }
+  trace_packet_defaults {
+    track_event_defaults {
+      track_uuid: 1
+      extra_counter_track_uuids: 10  # Counter "c1", defined below.
+    }
+  }
+}
+
+# Process track for the thread.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 0
+  track_descriptor {
+    uuid: 3
+    process {
+      pid: 5
+      process_name: "p1"
+    }
+  }
+}
+
+# Counter track "c1", a thread-scoped counter for "t1".
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 0
+  track_descriptor {
+    uuid: 10
+    parent_uuid: 1
+    counter {
+      type: 1                # COUNTER_THREAD_TIME_NS.
+      unit_multiplier: 1000  # provided in ys.
+      is_incremental: true   # use delta encoding.
+    }
+  }
+}
+
+# Sequence 2 has no defaults. Define a new global counter "MySizeCounter".
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 0
+  incremental_state_cleared: true
+  track_descriptor {
+    uuid: 11
+    name: "MySizeCounter"
+    counter {
+      unit: 3  # UNIT_SIZE_BYTES.
+    }
+  }
+}
+
+# Should appear on default track "t1" with extra_counter_values for "c1".
+packet {
+  trusted_packet_sequence_id: 1
+  sequence_flags: 2  # SEQ_NEEDS_INCREMENTAL_STATE
+  timestamp: 1000
+  track_event {
+    categories: "cat"
+    name: "event1_on_t1"
+    type: 1                     # TYPE_SLICE_BEGIN.
+    extra_counter_values: 1000  # First value, so effectively absolute.
+  }
+}
+
+# End for event above.
+packet {
+  trusted_packet_sequence_id: 1
+  sequence_flags: 2  # SEQ_NEEDS_INCREMENTAL_STATE
+  timestamp: 1100
+  track_event {
+    type: 2                   # TYPE_SLICE_END.
+    extra_counter_values: 10  # Absolute: 1010.
+  }
+}
+
+# Resetting incremental state on sequence 1 will restart counter at 0.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 2000
+  incremental_state_cleared: true
+  track_descriptor {
+    uuid: 1
+    parent_uuid: 3
+    thread {
+      pid: 5
+      tid: 1
+      thread_name: "t1"
+    }
+  }
+  trace_packet_defaults {
+    track_event_defaults {
+      track_uuid: 1
+      extra_counter_track_uuids: 10  # Counter "c1", defined below.
+    }
+  }
+}
+
+# Reemit process track for the thread.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 2000
+  track_descriptor {
+    uuid: 3
+    process {
+      pid: 5
+      process_name: "p1"
+    }
+  }
+}
+
+# Reemit counter descriptor, too.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 2000
+  track_descriptor {
+    uuid: 10
+    parent_uuid: 1
+    counter {
+      type: 1                # COUNTER_THREAD_TIME_NS.
+      unit_multiplier: 1000  # provided in ys.
+      is_incremental: true   # use delta encoding.
+    }
+  }
+}
+
+# Should appear on default track "t1" with extra_counter_values for "c1".
+packet {
+  trusted_packet_sequence_id: 1
+  sequence_flags: 2  # SEQ_NEEDS_INCREMENTAL_STATE
+  timestamp: 2000
+  track_event {
+    categories: "cat"
+    name: "event2_on_t1"
+    type: 1                     # TYPE_SLICE_BEGIN.
+    extra_counter_values: 2000  # First value after reset, so absolute.
+  }
+}
+
+# End for event above.
+packet {
+  trusted_packet_sequence_id: 1
+  sequence_flags: 2  # SEQ_NEEDS_INCREMENTAL_STATE
+  timestamp: 2200
+  track_event {
+    type: 2                   # TYPE_SLICE_END.
+    extra_counter_values: 10  # Absolute: 2010.
+  }
+}
+
+# Counter type event for "MySizeCounter" on sequence 1.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 3000
+  track_event {
+    track_uuid: 11       # "MySizeCounter".
+    type: 4              # TYPE_COUNTER.
+    counter_value: 1024  # Absolute.
+  }
+}
+
+# Counter type event for "MySizeCounter" on sequence 2.
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 3100
+  track_event {
+    track_uuid: 11       # "MySizeCounter".
+    type: 4              # TYPE_COUNTER.
+    counter_value: 2048  # Absolute.
+  }
+}
+
+# Override the default extra_counter_values.
+packet {
+  trusted_packet_sequence_id: 1
+  sequence_flags: 2  # SEQ_NEEDS_INCREMENTAL_STATE
+  timestamp: 4000
+  track_event {
+    categories: "cat"
+    name: "event3_on_t1"
+    type: 3                        # TYPE_INSTANT.
+    extra_counter_track_uuids: 10  # "c1".
+    extra_counter_track_uuids: 11  # "MySizeCounter".
+    extra_counter_values: 10       # Absolute: 2020.
+    extra_counter_values: 1024     # Absolute: 2020.
+  }
+}
+
+# Sequence 3 defaults to track for "t4" and uses legacy thread time and
+# instruction count.
+packet {
+  trusted_packet_sequence_id: 3
+  timestamp: 0
+  incremental_state_cleared: true
+  track_descriptor {
+    uuid: 4
+    parent_uuid: 3
+    thread {
+      pid: 5
+      tid: 4
+      thread_name: "t4"
+    }
+  }
+  trace_packet_defaults {
+    track_event_defaults {
+      track_uuid: 4
+    }
+  }
+}
+
+packet {
+  trusted_packet_sequence_id: 3
+  timestamp: 4000
+  track_event {
+    categories: "cat"
+    name: "event1_on_t3"
+    type: 1                        # TYPE_SLICE_BEGIN.
+    thread_time_absolute_us: 10
+    thread_instruction_count_absolute: 20
+  }
+}
+
+packet {
+  trusted_packet_sequence_id: 3
+  timestamp: 4100
+  track_event {
+    categories: "cat"
+    name: "event1_on_t3"
+    type: 2                        # TYPE_SLICE_END.
+    thread_time_absolute_us: 15
+    thread_instruction_count_absolute: 25
+  }
+}
diff --git a/test/trace_processor/track_event_counters_counters.out b/test/trace_processor/track_event_counters_counters.out
new file mode 100644
index 0000000..d5cc7b2
--- /dev/null
+++ b/test/trace_processor/track_event_counters_counters.out
@@ -0,0 +1,13 @@
+"counter_name","process","thread","thread_process","unit","track_args","ts","value"
+"thread_time","[NULL]","t1","p1","ns",1,1000,1000000.000000
+"thread_time","[NULL]","t1","p1","ns",1,1100,1010000.000000
+"thread_time","[NULL]","t1","p1","ns",1,2000,2000000.000000
+"thread_time","[NULL]","t1","p1","ns",1,2200,2010000.000000
+"MySizeCounter","[NULL]","[NULL]","[NULL]","bytes",2,3000,1024.000000
+"MySizeCounter","[NULL]","[NULL]","[NULL]","bytes",2,3100,2048.000000
+"thread_time","[NULL]","t1","p1","ns",1,4000,2020000.000000
+"MySizeCounter","[NULL]","[NULL]","[NULL]","bytes",2,4000,1024.000000
+"thread_time","[NULL]","t4","p1","[NULL]","[NULL]",4000,10000.000000
+"thread_instruction_count","[NULL]","t4","p1","[NULL]","[NULL]",4000,20.000000
+"thread_time","[NULL]","t4","p1","[NULL]","[NULL]",4100,15000.000000
+"thread_instruction_count","[NULL]","t4","p1","[NULL]","[NULL]",4100,25.000000
diff --git a/test/trace_processor/track_event_counters_slices.out b/test/trace_processor/track_event_counters_slices.out
new file mode 100644
index 0000000..f17c0b4
--- /dev/null
+++ b/test/trace_processor/track_event_counters_slices.out
@@ -0,0 +1,5 @@
+"track","process","thread","thread_process","ts","dur","category","name","arg_set_id"
+"[NULL]","[NULL]","t1","p1",1000,100,"cat","event1_on_t1",0
+"[NULL]","[NULL]","t1","p1",2000,200,"cat","event2_on_t1",0
+"[NULL]","[NULL]","t1","p1",4000,0,"cat","event3_on_t1",0
+"[NULL]","[NULL]","t4","p1",4000,100,"cat","event1_on_t3",0
diff --git a/test/trace_processor/track_event_merged_debug_annotations.textproto b/test/trace_processor/track_event_merged_debug_annotations.textproto
new file mode 100644
index 0000000..41d3565
--- /dev/null
+++ b/test/trace_processor/track_event_merged_debug_annotations.textproto
@@ -0,0 +1,117 @@
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 0
+  incremental_state_cleared: true
+  track_descriptor {
+    uuid: 1
+    thread {
+      pid: 5
+      tid: 1
+      thread_name: "t1"
+    }
+  }
+}
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 0
+  incremental_state_cleared: true
+  track_descriptor {
+    uuid: 1
+    thread {
+      pid: 5
+      tid: 5
+      thread_name: "t1"
+    }
+  }
+}
+packet {
+  # Emitted on tid 1.
+  # Should set the legacy_passthrough_utid to utid 1.
+  trusted_packet_sequence_id: 1
+  timestamp: 1000
+  track_event {
+    categories: "cat"
+    name: "name1"
+    debug_annotations {
+      name: "debug1"
+      nested_value {
+        nested_type: 1  # DICT
+        dict_keys: "key1"
+        dict_keys: "key2"
+        dict_values {
+          nested_type: 0  # leaf
+          int_value: 10
+        }
+        dict_values {
+          nested_type: 2  # ARRAY
+          array_values {
+            nested_type: 0  # leaf
+            int_value: 20
+          }
+          array_values {
+            nested_type: 0  # leaf
+            int_value: 21
+          }
+        }
+      }
+    }
+    debug_annotations {
+      name: "debug2"
+      legacy_json_value: "{\"key1\": 10, \"key2\": [20, 21],"
+                         " \"key3\": {\"key31\": 31}}"
+    }
+    debug_annotations {
+      name: "debug3"
+      int_value: 31
+    }
+    legacy_event {
+      phase: 98  # 'b'
+      global_id: 1234
+    }
+  }
+}
+packet {
+  # Emitted on tid 5.
+  # Should NOT override the legacy_passthrough_utid from the BEGIN event.
+  trusted_packet_sequence_id: 2
+  timestamp: 2000
+  track_event {
+    categories: "cat"
+    debug_annotations {
+      name: "debug1"
+      nested_value {
+        nested_type: 1  # DICT
+        dict_keys: "key3"
+        dict_keys: "key2"
+        dict_values {
+          nested_type: 0  # leaf
+          int_value: 30
+        }
+        dict_values {
+          nested_type: 2  # ARRAY
+          array_values {
+            nested_type: 0  # leaf
+            int_value: 22
+          }
+          array_values {
+            nested_type: 0  # leaf
+            int_value: 23
+          }
+        }
+      }
+    }
+    debug_annotations {
+      name: "debug2"
+      legacy_json_value: "{\"key4\": 40, \"key2\": [22, 23],"
+                         " \"key3\": {\"key32\": 32}}"
+    }
+    debug_annotations {
+      name: "debug3"
+      int_value: 32
+    }
+    legacy_event {
+      phase: 101  # 'e'
+      global_id: 1234
+    }
+  }
+}
diff --git a/test/trace_processor/track_event_merged_debug_annotations_args.out b/test/trace_processor/track_event_merged_debug_annotations_args.out
new file mode 100644
index 0000000..5cb53f9
--- /dev/null
+++ b/test/trace_processor/track_event_merged_debug_annotations_args.out
@@ -0,0 +1,21 @@
+"arg_set_id","flat_key","key","int_value","string_value"
+1,"source","source","[NULL]","chrome"
+1,"source_id","source_id",1234,"[NULL]"
+1,"source_id_is_process_scoped","source_id_is_process_scoped",0,"[NULL]"
+1,"source_scope","source_scope","[NULL]","cat"
+2,"debug1.key1","debug1.key1",10,"[NULL]"
+2,"debug1.key2","debug1.key2[0]",20,"[NULL]"
+2,"debug1.key2","debug1.key2[1]",21,"[NULL]"
+2,"debug1.key2","debug1.key2[2]",22,"[NULL]"
+2,"debug1.key2","debug1.key2[3]",23,"[NULL]"
+2,"debug1.key3","debug1.key3",30,"[NULL]"
+2,"debug2.key1","debug2.key1",10,"[NULL]"
+2,"debug2.key2","debug2.key2[0]",20,"[NULL]"
+2,"debug2.key2","debug2.key2[1]",21,"[NULL]"
+2,"debug2.key2","debug2.key2[2]",22,"[NULL]"
+2,"debug2.key2","debug2.key2[3]",23,"[NULL]"
+2,"debug2.key3.key31","debug2.key3.key31",31,"[NULL]"
+2,"debug2.key3.key32","debug2.key3.key32",32,"[NULL]"
+2,"debug2.key4","debug2.key4",40,"[NULL]"
+2,"debug3","debug3",32,"[NULL]"
+2,"legacy_event.passthrough_utid","legacy_event.passthrough_utid",1,"[NULL]"
diff --git a/test/trace_processor/track_event_tracks.textproto b/test/trace_processor/track_event_tracks.textproto
index b76fa36..65c8401 100644
--- a/test/trace_processor/track_event_tracks.textproto
+++ b/test/trace_processor/track_event_tracks.textproto
@@ -269,10 +269,26 @@
   track_event {
     track_uuid: 0
     categories: "cat"
-    name: "event1_on_t1"
+    name: "event2_on_p2"
     legacy_event {
       phase: 73               # 'I'
       instant_event_scope: 2  # Process scope
     }
   }
 }
+
+# And pid/tid overrides take effect even for TrackEvent type events.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 32000
+  track_event {
+    track_uuid: 0
+    categories: "cat"
+    name: "event2_on_t4"
+    type: 3
+    legacy_event {
+      pid_override: 5
+      tid_override: 4
+    }
+  }
+}
diff --git a/test/trace_processor/track_event_tracks_slices.out b/test/trace_processor/track_event_tracks_slices.out
index a031897..1471f76 100644
--- a/test/trace_processor/track_event_tracks_slices.out
+++ b/test/trace_processor/track_event_tracks_slices.out
@@ -2,12 +2,13 @@
 "[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",0
-"async","p1","[NULL]","[NULL]",5000,0,"cat","event1_on_async",0
-"async2","p1","[NULL]","[NULL]",5100,100,"cat","event1_on_async2",0
+"[NULL]","p1","[NULL]","[NULL]",4000,0,"cat","event1_on_p1",3
+"async","p1","[NULL]","[NULL]",5000,0,"cat","event1_on_async",3
+"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",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",5
+"[NULL]","p2","[NULL]","[NULL]",31000,0,"cat","event2_on_p2",6
+"[NULL]","[NULL]","t4","p2",32000,0,"cat","event2_on_t4",0
diff --git a/test/trace_processor/track_event_typed_args.textproto b/test/trace_processor/track_event_typed_args.textproto
index 3c49c21..67e37b2 100644
--- a/test/trace_processor/track_event_typed_args.textproto
+++ b/test/trace_processor/track_event_typed_args.textproto
@@ -51,3 +51,24 @@
     }
   }
 }
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 4000
+  track_event {
+    track_uuid: 1
+    categories: "cat"
+    name: "name4"
+    type: 3
+    chrome_latency_info {
+      trace_id: 7
+      component_info {
+        component_type: 3
+        time_us: 1201
+      }
+      component_info {
+        time_us: 928310
+      }
+      is_coalesced: true
+    }
+  }
+}
diff --git a/test/trace_processor/track_event_typed_args_args.out b/test/trace_processor/track_event_typed_args_args.out
index c66424f..12173de 100644
--- a/test/trace_processor/track_event_typed_args_args.out
+++ b/test/trace_processor/track_event_typed_args_args.out
@@ -3,3 +3,8 @@
 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"
+4,"latency_info.component_info.component_type","latency_info.component_info[0].component_type","[NULL]","COMPONENT_INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL"
+4,"latency_info.component_info.time_us","latency_info.component_info[0].time_us",1201,"[NULL]"
+4,"latency_info.component_info.time_us","latency_info.component_info[1].time_us",928310,"[NULL]"
+4,"latency_info.is_coalesced","latency_info.is_coalesced",1,"[NULL]"
+4,"latency_info.trace_id","latency_info.trace_id",7,"[NULL]"
diff --git a/test/trace_processor/track_event_typed_args_slices.out b/test/trace_processor/track_event_typed_args_slices.out
index 830accb..84026f7 100644
--- a/test/trace_processor/track_event_typed_args_slices.out
+++ b/test/trace_processor/track_event_typed_args_slices.out
@@ -2,3 +2,4 @@
 "[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
+"[NULL]","[NULL]","t1","[NULL]",4000,0,"cat","name4",4
diff --git a/test/trace_processor/track_event_with_atrace.out b/test/trace_processor/track_event_with_atrace.out
new file mode 100644
index 0000000..5f12ba4
--- /dev/null
+++ b/test/trace_processor/track_event_with_atrace.out
@@ -0,0 +1,4 @@
+"track","process","thread","thread_process","ts","dur","category","name","arg_set_id"
+"[NULL]","[NULL]","t1","[NULL]",10000,1000,"cat","event1",0
+"[NULL]","[NULL]","t1","[NULL]",20000,8000,"cat","event2",0
+"[NULL]","[NULL]","t1","[NULL]",21000,7000,"[NULL]","atrace",0
diff --git a/test/trace_processor/track_event_with_atrace.textproto b/test/trace_processor/track_event_with_atrace.textproto
new file mode 100644
index 0000000..5d42779
--- /dev/null
+++ b/test/trace_processor/track_event_with_atrace.textproto
@@ -0,0 +1,103 @@
+# Sequence 1 defaults to track for "t1".
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 0
+  incremental_state_cleared: true
+  track_descriptor {
+    uuid: 1
+    thread {
+      pid: 5
+      tid: 1
+      thread_name: "t1"
+    }
+  }
+  trace_packet_defaults {
+    track_event_defaults {
+      track_uuid: 1
+    }
+  }
+}
+
+# Track event with category.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 10000
+  track_event {
+    track_uuid: 0
+    categories: "cat"
+    name: "event1"
+    legacy_event {
+      phase: 66  # 'B'
+    }
+  }
+}
+
+# End track event without category.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 11000
+  track_event {
+    track_uuid: 0
+    legacy_event {
+      phase: 69  # 'E'
+    }
+  }
+}
+
+# Another track event.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 20000
+  track_event {
+    track_uuid: 0
+    categories: "cat"
+    name: "event2"
+    legacy_event {
+      phase: 66  # 'B'
+    }
+  }
+}
+
+# atrace event on the same track
+packet {
+  ftrace_events {
+    cpu: 1
+    event {
+      timestamp: 21000
+      pid: 1
+      print {
+        ip: 1
+        buf: "B|5|atrace\n"
+      }
+    }
+  }
+}
+
+# End track event.  Packet is out of order, but shouldn't crash because
+# category is specified.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 28000
+  track_event {
+    track_uuid: 0
+    categories: "cat"
+    legacy_event {
+      phase: 69  # 'E'
+    }
+  }
+}
+
+# End atrace event
+packet {
+  ftrace_events {
+    cpu: 1
+    event {
+      timestamp: 29000
+      pid: 1
+      print {
+        ip: 1
+        buf: "E|5\n"
+      }
+    }
+  }
+}
diff --git a/test/trace_processor/trigger_packet_trace.textproto b/test/trace_processor/trigger_packet_trace.textproto
new file mode 100644
index 0000000..11148ab
--- /dev/null
+++ b/test/trace_processor/trigger_packet_trace.textproto
@@ -0,0 +1,16 @@
+packet {
+  trigger {
+    trigger_name: "test1"
+    trusted_producer_uid: 3
+    producer_name: "producer1"
+  }
+  timestamp: 101000002
+}
+packet {
+  trigger {
+    trigger_name: "test2"
+    trusted_producer_uid: 4
+    producer_name: "producer2"
+  }
+  timestamp: 101000004
+}
diff --git a/test/trace_processor/triggers_packets.sql b/test/trace_processor/triggers_packets.sql
new file mode 100644
index 0000000..89d5cdb
--- /dev/null
+++ b/test/trace_processor/triggers_packets.sql
@@ -0,0 +1,45 @@
+--
+-- Copyright 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
+--
+--     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
+  ts,
+  name,
+  string_value,
+  int_value
+FROM (
+  SELECT
+    slice.arg_set_id,
+    slice.track_id,
+    slice.ts,
+    slice.name,
+    prod.string_value
+  FROM slice JOIN (
+    SELECT
+      arg_set_id,
+      string_value
+    FROM args
+    WHERE key = "producer_name"
+  ) prod ON slice.arg_set_id = prod.arg_set_id
+) slice_prod JOIN (
+  SELECT
+    arg_set_id,
+    int_value
+  FROM args
+  WHERE key = "trusted_producer_uid"
+) prod_uid ON prod_uid.arg_set_id = slice_prod.arg_set_id
+WHERE slice_prod.track_id in (
+  SELECT id FROM track WHERE name = "Trace Triggers"
+)
+ORDER BY ts ASC;
diff --git a/test/trace_processor/triggers_packets_trigger_packet_trace.out b/test/trace_processor/triggers_packets_trigger_packet_trace.out
new file mode 100644
index 0000000..aafd335
--- /dev/null
+++ b/test/trace_processor/triggers_packets_trigger_packet_trace.out
@@ -0,0 +1,3 @@
+"ts","name","string_value","int_value"
+101000002,"test1","producer1",3
+101000004,"test2","producer2",4
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/build_all_configs.py b/tools/build_all_configs.py
index 14ebdd1..c1e22a0 100755
--- a/tools/build_all_configs.py
+++ b/tools/build_all_configs.py
@@ -69,13 +69,13 @@
   parser = argparse.ArgumentParser()
   parser.add_argument('--ccache', action='store_true', default=False)
   parser.add_argument('--host-only', action='store_true', default=False)
-  parser.add_argument('--no-android', action='store_true', default=False)
+  parser.add_argument('--android', action='store_true', default=False)
   parser.add_argument('--build', metavar='TARGET')
   args = parser.parse_args()
 
   configs = {}
   if not args.host_only:
-    if not args.no_android:
+    if args.android:
       for config_name, gn_args in iteritems(ANDROID_BUILD_CONFIGS):
         for arch in ANDROID_ARCHS:
           full_config_name = '%s_%s' % (config_name, arch)
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/dev_server b/tools/dev_server
index bd74df7..fb836b8 100755
--- a/tools/dev_server
+++ b/tools/dev_server
@@ -68,6 +68,7 @@
           self.send_response(200)
           self.send_header("Content-type", "text/html")
           self.end_headers()
+          self.wfile.write("<pre>")
           self.wfile.write(e.stdout_and_stderr)
           return
         return SimpleHTTPRequestHandler.do_GET(self)
diff --git a/tools/diff_test_trace_processor.py b/tools/diff_test_trace_processor.py
index 2d92f39..ea1f5b5 100755
--- a/tools/diff_test_trace_processor.py
+++ b/tools/diff_test_trace_processor.py
@@ -34,18 +34,20 @@
 
 class Test(object):
 
-  def __init__(self, trace_fname, query_fname_or_metric, expected_fname):
-    self.trace_fname = trace_fname
-    self.query_fname_or_metric = query_fname_or_metric
-    self.expected_fname = expected_fname
+  def __init__(self, type, trace_path, query_path_or_metric, expected_path):
+    self.type = type
+    self.trace_path = trace_path
+    self.query_path_or_metric = query_path_or_metric
+    self.expected_path = expected_path
 
 
 class PerfResult(object):
 
-  def __init__(self, trace_name, query_or_metric, ingest_time_ns_str,
-               real_time_ns_str):
-    self.trace_name = trace_name
-    self.query_or_metric = query_or_metric
+  def __init__(self, test_type, trace_path, query_path_or_metric,
+               ingest_time_ns_str, real_time_ns_str):
+    self.test_type = test_type
+    self.trace_path = trace_path
+    self.query_path_or_metric = query_path_or_metric
     self.ingest_time_ns = int(ingest_time_ns_str)
     self.real_time_ns = int(real_time_ns_str)
 
@@ -102,7 +104,7 @@
 class TestResult(object):
 
   def __init__(self, test_type, input_name, trace, cmd, expected, actual,
-               stderr):
+               stderr, exit_code):
     self.test_type = test_type
     self.input_name = input_name
     self.trace = trace
@@ -110,6 +112,7 @@
     self.expected = expected
     self.actual = actual
     self.stderr = stderr
+    self.exit_code = exit_code
 
 
 def run_metrics_test(trace_processor_path, gen_trace_path, metric,
@@ -149,7 +152,7 @@
     actual_text = text_format.MessageToString(actual_message)
 
   return TestResult('metric', metric, gen_trace_path, cmd, expected_text,
-                    actual_text, stderr)
+                    actual_text, stderr, tp.returncode)
 
 
 def run_query_test(trace_processor_path, gen_trace_path, query_path,
@@ -169,17 +172,16 @@
   tp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
   (stdout, stderr) = tp.communicate()
   return TestResult('query', query_path, gen_trace_path, cmd, expected, stdout,
-                    stderr)
+                    stderr, tp.returncode)
 
 
-def run_all_tests(args, test_dir, index_dir, trace_descriptor_path,
-                  metrics_message_factory, tests):
+def run_all_tests(trace_processor, trace_descriptor_path,
+                  metrics_message_factory, tests, keep_input):
   perf_data = []
   test_failure = 0
   for test in tests:
-    trace_path = os.path.abspath(os.path.join(index_dir, test.trace_fname))
-    expected_path = os.path.abspath(
-        os.path.join(index_dir, test.expected_fname))
+    trace_path = test.trace_path
+    expected_path = test.expected_path
     if not os.path.exists(trace_path):
       sys.stderr.write('Trace file not found {}\n'.format(trace_path))
       test_failure += 1
@@ -190,12 +192,12 @@
       continue
 
     if trace_path.endswith('.py'):
-      gen_trace_file = tempfile.NamedTemporaryFile()
+      gen_trace_file = tempfile.NamedTemporaryFile(delete=False)
       python_cmd = ['python', trace_path, trace_descriptor_path]
       subprocess.check_call(python_cmd, stdout=gen_trace_file)
       gen_trace_path = os.path.realpath(gen_trace_file.name)
     elif trace_path.endswith('.textproto'):
-      gen_trace_file = tempfile.NamedTemporaryFile()
+      gen_trace_file = tempfile.NamedTemporaryFile(delete=False)
       serialize_text_proto_to_file(trace_descriptor_path, trace_path,
                                    gen_trace_file)
       gen_trace_path = os.path.realpath(gen_trace_file.name)
@@ -205,23 +207,23 @@
 
     with tempfile.NamedTemporaryFile() as tmp_perf_file:
       sys.stderr.write('[ RUN      ] {} {}\n'.format(
-          os.path.basename(test.query_fname_or_metric),
+          os.path.basename(test.query_path_or_metric),
           os.path.basename(trace_path)))
 
       tmp_perf_path = tmp_perf_file.name
-      if args.test_type == 'queries':
-        query_path = os.path.abspath(
-            os.path.join(index_dir, test.query_fname_or_metric))
-        if not os.path.exists(query_path):
+      if test.type == 'queries':
+        query_path = test.query_path_or_metric
+
+        if not os.path.exists(test.query_path_or_metric):
           print('Query file not found {}'.format(query_path))
           test_failure += 1
           continue
 
-        result = run_query_test(args.trace_processor, gen_trace_path,
-                                query_path, expected_path, tmp_perf_path)
-      elif args.test_type == 'metrics':
-        result = run_metrics_test(args.trace_processor, gen_trace_path,
-                                  test.query_fname_or_metric, expected_path,
+        result = run_query_test(trace_processor, gen_trace_path, query_path,
+                                expected_path, tmp_perf_path)
+      elif test.type == 'metrics':
+        result = run_metrics_test(trace_processor, gen_trace_path,
+                                  test.query_path_or_metric, expected_path,
                                   tmp_perf_path, metrics_message_factory)
       else:
         assert False
@@ -229,48 +231,94 @@
       perf_lines = tmp_perf_file.readlines()
 
     if gen_trace_file:
-      gen_trace_file.close()
+      if keep_input:
+        print "Saving generated input trace: ", gen_trace_path
+      else:
+        gen_trace_file.close()
+        os.remove(gen_trace_path)
 
-    if result.expected == result.actual:
+    if result.exit_code != 0 or result.expected != result.actual:
+      sys.stderr.write(result.stderr)
+
+      if result.exit_code == 0:
+        sys.stderr.write(
+            'Expected did not match actual for trace {} and {} {}\n'.format(
+                trace_path, result.test_type, result.input_name))
+        sys.stderr.write('Expected file: {}\n'.format(expected_path))
+        sys.stderr.write('Command line: {}\n'.format(' '.join(result.cmd)))
+
+        write_diff(result.expected, result.actual)
+      else:
+        sys.stderr.write('Command line: {}\n'.format(' '.join(result.cmd)))
+
+      sys.stderr.write('[     FAIL ] {} {}\n'.format(
+          os.path.basename(test.query_path_or_metric),
+          os.path.basename(trace_path)))
+
+      test_failure += 1
+    else:
       assert len(perf_lines) == 1
       perf_numbers = perf_lines[0].split(',')
 
-      trace_shortpath = os.path.relpath(trace_path, test_dir)
-
       assert len(perf_numbers) == 2
-      perf_result = PerfResult(trace_shortpath, test.query_fname_or_metric,
+      perf_result = PerfResult(test.type, trace_path, test.query_path_or_metric,
                                perf_numbers[0], perf_numbers[1])
       perf_data.append(perf_result)
 
       sys.stderr.write(
           '[       OK ] {} {} (ingest: {} ms, query: {} ms)\n'.format(
-              os.path.basename(test.query_fname_or_metric),
+              os.path.basename(test.query_path_or_metric),
               os.path.basename(trace_path),
               perf_result.ingest_time_ns / 1000000,
               perf_result.real_time_ns / 1000000))
-    else:
-      sys.stderr.write(result.stderr)
-
-      sys.stderr.write(
-          'Expected did not match actual for trace {} and {} {}\n'.format(
-              trace_path, result.test_type, result.input_name))
-      sys.stderr.write('Expected file: {}\n'.format(expected_path))
-      sys.stderr.write('Command line: {}\n'.format(' '.join(result.cmd)))
-
-      write_diff(result.expected, result.actual)
-
-      sys.stderr.write('[     FAIL ] {} {}\n'.format(
-          os.path.basename(test.query_fname_or_metric),
-          os.path.basename(trace_path)))
-
-      test_failure += 1
 
   return test_failure, perf_data
 
 
+def read_all_tests(test_type, query_metric_pattern, trace_pattern):
+  if test_type == 'queries':
+    index = os.path.join(ROOT_DIR, 'test', 'trace_processor', 'index')
+  elif test_type == 'metrics':
+    index = os.path.join(ROOT_DIR, 'test', 'metrics', 'index')
+  else:
+    assert False
+
+  index_dir = os.path.dirname(index)
+  with open(index, 'r') as file:
+    index_lines = file.readlines()
+
+  tests = []
+  for line in index_lines:
+    stripped = line.strip()
+    if stripped.startswith('#'):
+      continue
+    elif not stripped:
+      continue
+
+    [trace_fname, query_fname_or_metric, expected_fname] = stripped.split(' ')
+    if not query_metric_pattern.match(os.path.basename(query_fname_or_metric)):
+      continue
+
+    if not trace_pattern.match(os.path.basename(trace_fname)):
+      continue
+
+    trace_path = os.path.abspath(os.path.join(index_dir, trace_fname))
+    expected_path = os.path.abspath(os.path.join(index_dir, expected_fname))
+
+    query_path_or_metric = query_fname_or_metric
+    if test_type == 'queries':
+      query_path_or_metric = os.path.abspath(
+          os.path.join(index_dir, query_fname_or_metric))
+
+    tests.append(
+        Test(test_type, trace_path, query_path_or_metric, expected_path))
+
+  return tests
+
+
 def main():
   parser = argparse.ArgumentParser()
-  parser.add_argument('--test-type', type=str, default='queries')
+  parser.add_argument('--test-type', type=str, default='all')
   parser.add_argument('--trace-descriptor', type=str)
   parser.add_argument('--metrics-descriptor', type=str)
   parser.add_argument('--perf-file', type=str)
@@ -286,22 +334,30 @@
       type=str,
       help='Filter the name of trace files to diff test (regex syntax)')
   parser.add_argument(
+      '--keep-input',
+      action='store_true',
+      help='Save the (generated) input pb file for debugging')
+  parser.add_argument(
       'trace_processor', type=str, help='location of trace processor binary')
   args = parser.parse_args()
 
-  test_dir = os.path.join(ROOT_DIR, 'test')
-  if args.test_type == 'queries':
-    index = os.path.join(test_dir, 'trace_processor', 'index')
-  elif args.test_type == 'metrics':
-    index = os.path.join(test_dir, 'metrics', 'index')
-  else:
-    print('Unknown test type {}. Supported: queries, metircs'.format(
-        args.test_type))
+  test_type = args.test_type
+  if test_type != 'all' and test_type != 'queries' and test_type != 'metrics':
+    print('Unknown test type {}. Supported: all, queries, metrics'.format(
+        test_type))
     return 1
 
-  index_dir = os.path.dirname(index)
-  with open(index, 'r') as file:
-    index_lines = file.readlines()
+  query_metric_pattern = re.compile(args.query_metric_filter)
+  trace_pattern = re.compile(args.trace_filter)
+
+  tests = []
+  if test_type == 'all' or test_type == 'metrics':
+    tests += read_all_tests('metrics', query_metric_pattern, trace_pattern)
+
+  if test_type == 'all' or test_type == 'queries':
+    tests += read_all_tests('queries', query_metric_pattern, trace_pattern)
+
+  sys.stderr.write('[==========] Running {} tests.\n'.format(len(tests)))
 
   if args.trace_descriptor:
     trace_descriptor_path = args.trace_descriptor
@@ -323,32 +379,10 @@
   metrics_message_factory = create_metrics_message_factory(
       metrics_descriptor_path)
 
-  query_metric_pattern = re.compile(args.query_metric_filter)
-  trace_pattern = re.compile(args.trace_filter)
-
-  tests = []
-  for line in index_lines:
-    stripped = line.strip()
-    if stripped.startswith('#'):
-      continue
-    elif not stripped:
-      continue
-
-    [trace_fname, query_fname_or_metric, expected_fname] = stripped.split(' ')
-    if not query_metric_pattern.match(os.path.basename(query_fname_or_metric)):
-      continue
-
-    if not trace_pattern.match(os.path.basename(trace_fname)):
-      continue
-
-    tests.append(Test(trace_fname, query_fname_or_metric, expected_fname))
-
-  sys.stderr.write('[==========] Running {} tests.\n'.format(len(tests)))
-
   test_run_start = datetime.datetime.now()
-  test_failure, perf_data = run_all_tests(args, test_dir, index_dir,
-                                          trace_descriptor_path,
-                                          metrics_message_factory, tests)
+  test_failure, perf_data = run_all_tests(
+      args.trace_processor, trace_descriptor_path, metrics_message_factory,
+      tests, args.keep_input)
   test_run_end = datetime.datetime.now()
 
   sys.stderr.write('[==========] {} tests ran. ({} ms total)\n'.format(
@@ -357,33 +391,47 @@
 
   if test_failure == 0:
     if args.perf_file:
-      metrics = [[{
-          'metric': 'tp_perf_test_ingest_time',
-          'value': float(perf_args.ingest_time_ns) / 1.0e9,
-          'unit': 's',
-          'tags': {
-              'test_name':
-                  '{}-{}'.format(perf_args.trace_name,
-                                 perf_args.query_or_metric),
-              'test_type':
-                  args.test_type,
-          },
-          'labels': {},
-      },
-                  {
-                      'metric': 'perf_test_real_time',
-                      'value': float(perf_args.real_time_ns) / 1.0e9,
-                      'unit': 's',
-                      'tags': {
-                          'test_name':
-                              '{}-{}'.format(perf_args.trace_name,
-                                             perf_args.query_or_metric),
-                          'test_type':
-                              args.test_type,
-                      },
-                      'labels': {},
-                  }] for perf_args in sorted(perf_data)]
-      output_data = {'metrics': list(chain.from_iterable(metrics))}
+      test_dir = os.path.join(ROOT_DIR, 'test')
+      trace_processor_dir = os.path.join(test_dir, 'trace_processor')
+
+      metrics = []
+      for perf_args in sorted(perf_data):
+        trace_short_path = os.path.relpath(perf_args.trace_path, test_dir)
+
+        query_short_path_or_metric = perf_args.query_path_or_metric
+        if perf_args.test_type == 'queries':
+          query_short_path_or_metric = os.path.relpath(
+              perf_args.query_path_or_metric, trace_processor_dir)
+
+        metrics.append({
+            'metric': 'tp_perf_test_ingest_time',
+            'value': float(perf_args.ingest_time_ns) / 1.0e9,
+            'unit': 's',
+            'tags': {
+                'test_name':
+                    '{}-{}'.format(trace_short_path,
+                                   query_short_path_or_metric),
+                'test_type':
+                    perf_args.test_type,
+            },
+            'labels': {},
+        })
+        metrics.append({
+            'metric': 'perf_test_real_time',
+            'value': float(perf_args.real_time_ns) / 1.0e9,
+            'unit': 's',
+            'tags': {
+                'test_name':
+                    '{}-{}'.format(
+                        os.path.relpath(perf_args.trace_path, test_dir),
+                        query_short_path_or_metric),
+                'test_type':
+                    perf_args.test_type,
+            },
+            'labels': {},
+        })
+
+      output_data = {'metrics': metrics}
       with open(args.perf_file, 'w+') as perf_file:
         perf_file.write(json.dumps(output_data, indent=2))
     return 0
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/extract_linux_syscall_tables b/tools/extract_linux_syscall_tables
index 4b2def0..d1753bf 100755
--- a/tools/extract_linux_syscall_tables
+++ b/tools/extract_linux_syscall_tables
@@ -44,6 +44,9 @@
   response = urllib2.urlopen(KSRC + 'arch/x86/entry/syscalls/syscall_64.tbl')
   syscalls['x86_64'] = parse_tlb(response.read())
 
+  response = urllib2.urlopen(KSRC + 'arch/x86/entry/syscalls/syscall_32.tbl')
+  syscalls['x86'] = parse_tlb(response.read())
+
   response = urllib2.urlopen(KSRC + 'arch/arm/tools/syscall.tbl')
   syscalls['armeabi'] = parse_tlb(response.read())
 
@@ -58,6 +61,7 @@
   syscalls['aarch64'] = parse_def(response.read())
 
   print_table('x86_64')
+  print_table('x86')
   print_table('aarch64')
   print_table('armeabi')
   print_table('aarch32')
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/event_whitelist b/tools/ftrace_proto_gen/event_whitelist
index c788314..f28511d 100644
--- a/tools/ftrace_proto_gen/event_whitelist
+++ b/tools/ftrace_proto_gen/event_whitelist
@@ -328,3 +328,5 @@
 systrace/0
 power/gpu_frequency
 sde/tracing_mark_write
+oom/mark_victim
+ion/ion_stat
diff --git a/tools/ftrace_proto_gen/ftrace_descriptor_gen.cc b/tools/ftrace_proto_gen/ftrace_descriptor_gen.cc
index 9384622..e230a9e 100644
--- a/tools/ftrace_proto_gen/ftrace_descriptor_gen.cc
+++ b/tools/ftrace_proto_gen/ftrace_descriptor_gen.cc
@@ -106,6 +106,14 @@
   return &descriptors[id];
 }
 
+MessageDescriptor* GetMessageDescriptorForName(base::StringView name) {
+  for (MessageDescriptor& descriptor : descriptors) {
+    if (descriptor.name != nullptr && descriptor.name == name)
+      return &descriptor;
+  }
+  return nullptr;
+}
+
 size_t GetDescriptorsSize() {
   return descriptors.size();
 }
diff --git a/tools/ftrace_proto_gen/proto_gen_utils.cc b/tools/ftrace_proto_gen/proto_gen_utils.cc
index 6fa9e3f..ba29c72 100644
--- a/tools/ftrace_proto_gen/proto_gen_utils.cc
+++ b/tools/ftrace_proto_gen/proto_gen_utils.cc
@@ -28,65 +28,24 @@
 #include "perfetto/ext/base/pipe.h"
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/base/subprocess.h"
 
 namespace perfetto {
 
 namespace {
 
 std::string RunClangFmt(const std::string& input) {
-  std::string output;
-  pid_t pid;
-  base::Pipe input_pipe = base::Pipe::Create(base::Pipe::kBothNonBlock);
-  base::Pipe output_pipe = base::Pipe::Create(base::Pipe::kBothNonBlock);
-  if ((pid = fork()) == 0) {
-    // Child
-    PERFETTO_CHECK(dup2(*input_pipe.rd, STDIN_FILENO) != -1);
-    PERFETTO_CHECK(dup2(*output_pipe.wr, STDOUT_FILENO) != -1);
-    input_pipe.wr.reset();
-    output_pipe.rd.reset();
-    PERFETTO_CHECK(execl("buildtools/linux64/clang-format", "clang-format",
-                         nullptr) != -1);
-  }
-  PERFETTO_CHECK(pid > 0);
-  // Parent
-  size_t written = 0;
-  size_t bytes_read = 0;
-  input_pipe.rd.reset();
-  output_pipe.wr.reset();
-  // This cannot be left uninitialized because there's as continue statement
-  // before the first assignment to this in the loop.
-  ssize_t r = -1;
-  do {
-    if (written < input.size()) {
-      ssize_t w =
-          write(*input_pipe.wr, &(input[written]), input.size() - written);
-      if (w == -1) {
-        if (errno == EAGAIN || errno == EINTR)
-          continue;
-        PERFETTO_FATAL("write failed");
-      }
-      written += static_cast<size_t>(w);
-      if (written == input.size())
-        input_pipe.wr.reset();
-    }
-
-    if (bytes_read + base::kPageSize > output.size())
-      output.resize(output.size() + base::kPageSize);
-    r = read(*output_pipe.rd, &(output[bytes_read]), base::kPageSize);
-    if (r == -1) {
-      if (errno == EAGAIN || errno == EINTR)
-        continue;
-      PERFETTO_FATAL("read failed");
-    }
-    if (r > 0)
-      bytes_read += static_cast<size_t>(r);
-  } while (r != 0);
-  output.resize(bytes_read);
-
-  int wstatus;
-  waitpid(pid, &wstatus, 0);
-  PERFETTO_CHECK(WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0);
-  return output;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
+  const std::string platform = "mac";
+#else
+  const std::string platform = "linux64";
+#endif
+  base::Subprocess clang_fmt({"buildtools/" + platform + "/clang-format"});
+  clang_fmt.args.stdout_mode = base::Subprocess::kBuffer;
+  clang_fmt.args.stderr_mode = base::Subprocess::kInherit;
+  clang_fmt.args.input = input;
+  PERFETTO_CHECK(clang_fmt.Call());
+  return std::move(clang_fmt.output());
 }
 
 }  // namespace
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index 97213a6..5567f04 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -63,6 +63,7 @@
     '//src/traced/service:traced',
     '//test/cts:perfetto_cts_deps',
     '//test/cts:perfetto_cts_jni_deps',
+    '//test:perfetto_gtest_logcat_printer',
 ]
 
 # Host targets
@@ -84,6 +85,7 @@
 target_initrc = {
     '//src/traced/service:traced': {'perfetto.rc'},
     '//src/profiling/memory:heapprofd': {'heapprofd.rc'},
+    '//src/profiling/perf:traced_perf': {'traced_perf.rc'},
 }
 
 target_host_supported = [
@@ -145,7 +147,7 @@
   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.
@@ -168,14 +170,12 @@
         ('include_dirs', {'bionic/libc/kernel'}),
     ],
     'traced_probes': [
-        ('required', {'libperfetto_android_internal', 'trigger_perfetto'}),
+        ('required', {'libperfetto_android_internal',
+                      'trigger_perfetto',
+                      'traced_perf'}),
     ],
     'libperfetto_android_internal': [('static_libs', {'libhealthhalutils'}),],
-    'traced_perf': [
-        ('include_dirs', {'bionic/libc/kernel'}),
-    ],
     'trace_processor_shell': [
-      ('dist', {'targets': ['sdk_repo']}),
       ('stl', 'libc++_static'),
     ],
 }
@@ -184,6 +184,8 @@
 def enable_gtest_and_gmock(module):
   module.static_libs.add('libgmock')
   module.static_libs.add('libgtest')
+  if module.name != 'perfetto_gtest_logcat_printer':
+    module.whole_static_libs.add('perfetto_gtest_logcat_printer')
 
 
 def enable_protobuf_full(module):
@@ -231,6 +233,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 = {
@@ -245,6 +251,7 @@
     '//gn:libunwindstack': enable_libunwindstack,
     '//gn:sqlite': enable_sqlite,
     '//gn:zlib': enable_zlib,
+    '//gn:bionic_kernel_uapi_headers' : enable_uapi_headers,
 }
 
 # ----------------------------------------------------------------------------
@@ -305,12 +312,14 @@
     self.name = name
     self.shared_libs = set()
     self.static_libs = set()
+    self.whole_static_libs = set()
     self.cflags = set()
 
   def to_string(self, output):
     nested_out = []
     self._output_field(nested_out, 'shared_libs')
     self._output_field(nested_out, 'static_libs')
+    self._output_field(nested_out, 'whole_static_libs')
     self._output_field(nested_out, 'cflags')
 
     if nested_out:
@@ -335,6 +344,7 @@
     self.comment = 'GN: ' + gn_utils.label_without_toolchain(gn_target)
     self.shared_libs = set()
     self.static_libs = set()
+    self.whole_static_libs = set()
     self.tools = set()
     self.cmd = None
     self.host_supported = False
@@ -355,6 +365,7 @@
     self.lto = None
     self.stl = None
     self.dist = dict()
+    self.strip = dict()
     self.data = set()
     # The genrule_XXX below are properties that must to be propagated back
     # on the module(s) that depend on the genrule.
@@ -370,6 +381,7 @@
     self._output_field(output, 'srcs')
     self._output_field(output, 'shared_libs')
     self._output_field(output, 'static_libs')
+    self._output_field(output, 'whole_static_libs')
     self._output_field(output, 'tools')
     self._output_field(output, 'cmd', sort=False)
     self._output_field(output, 'host_supported')
@@ -384,6 +396,7 @@
     self._output_field(output, 'header_libs')
     self._output_field(output, 'required')
     self._output_field(output, 'dist')
+    self._output_field(output, 'strip')
     self._output_field(output, 'tool_files')
     self._output_field(output, 'data')
     self._output_field(output, 'stl')
@@ -712,6 +725,8 @@
       module.generated_headers.update(dep_module.genrule_headers)
       module.srcs.update(dep_module.genrule_srcs)
       module.shared_libs.update(dep_module.genrule_shared_libs)
+    elif dep_module.type == 'cc_binary':
+      continue  # Ignore executables deps (used by cmdline integration tests).
     else:
       raise Error('Unknown dep %s (%s) for target %s' %
                   (dep_module.name, dep_module.type, module.name))
diff --git a/tools/gen_binary_descriptors b/tools/gen_binary_descriptors
index 839d7c0..ae752f8 100755
--- a/tools/gen_binary_descriptors
+++ b/tools/gen_binary_descriptors
@@ -31,8 +31,6 @@
         'src/perfetto_cmd/perfetto_config.descriptor.h',
     'protos/perfetto/metrics/metrics.proto':
         'src/trace_processor/metrics/metrics.descriptor.h',
-    'protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto':
-        'src/trace_processor/importers/proto/chrome_compositor_scheduler_state.descriptor.h',
     'src/protozero/test/example_proto/test_messages.proto':
         'src/protozero/test/example_proto/test_messages.descriptor.h',
     'protos/perfetto/trace/track_event/track_event.proto':
diff --git a/tools/gen_merged_protos b/tools/gen_merged_protos
index a8f9aa6..dee1920 100755
--- a/tools/gen_merged_protos
+++ b/tools/gen_merged_protos
@@ -21,108 +21,19 @@
 import sys
 from codecs import open
 
-COMMON_PROTOS = (
-    'protos/perfetto/common/android_log_constants.proto',
+PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+
+CONFIG_PROTO_ROOTS = [
     'protos/perfetto/common/data_source_descriptor.proto',
-    'protos/perfetto/common/gpu_counter_descriptor.proto',
-    'protos/perfetto/common/sys_stats_counters.proto',
-    'protos/perfetto/common/trace_stats.proto',
     'protos/perfetto/common/tracing_service_state.proto',
-    'protos/perfetto/common/track_event_descriptor.proto',
-)
-
-CONFIG_PROTOS = (
-    'protos/perfetto/config/android/android_log_config.proto',
-    'protos/perfetto/config/chrome/chrome_config.proto',
-    'protos/perfetto/config/data_source_config.proto',
-    'protos/perfetto/config/ftrace/ftrace_config.proto',
-    'protos/perfetto/config/inode_file/inode_file_config.proto',
-    'protos/perfetto/config/power/android_power_config.proto',
-    'protos/perfetto/config/process_stats/process_stats_config.proto',
-    'protos/perfetto/config/sys_stats/sys_stats_config.proto',
-    'protos/perfetto/config/test_config.proto',
-    'protos/perfetto/config/trace_config.proto',
-    'protos/perfetto/config/profiling/heapprofd_config.proto',
-    'protos/perfetto/config/profiling/java_hprof_config.proto',
-    'protos/perfetto/config/profiling/perf_event_config.proto',
-    'protos/perfetto/config/gpu/gpu_counter_config.proto',
-    'protos/perfetto/config/gpu/vulkan_memory_config.proto',
-    'protos/perfetto/config/android/packages_list_config.proto',
-)
-
+    'protos/perfetto/config/trace_config.proto'
+]
 MERGED_CONFIG_PROTO = 'protos/perfetto/config/perfetto_config.proto'
 
-TRACE_PROTOS = (
-    'protos/perfetto/trace/android/android_log.proto',
-    'protos/perfetto/trace/android/graphics_frame_event.proto',
-    'protos/perfetto/trace/android/packages_list.proto',
-    'protos/perfetto/trace/chrome/chrome_benchmark_metadata.proto',
-    "protos/perfetto/trace/chrome/chrome_metadata.proto",
-    'protos/perfetto/trace/clock_snapshot.proto',
-    'protos/perfetto/trace/filesystem/inode_file_map.proto',
-    'protos/perfetto/trace/ftrace/binder.proto',
-    'protos/perfetto/trace/ftrace/block.proto',
-    'protos/perfetto/trace/ftrace/clk.proto',
-    'protos/perfetto/trace/ftrace/ext4.proto',
-    'protos/perfetto/trace/ftrace/f2fs.proto',
-    'protos/perfetto/trace/ftrace/filemap.proto',
-    'protos/perfetto/trace/ftrace/ftrace.proto',
-    'protos/perfetto/trace/ftrace/ftrace_event.proto',
-    'protos/perfetto/trace/ftrace/ftrace_event_bundle.proto',
-    'protos/perfetto/trace/ftrace/ftrace_stats.proto',
-    'protos/perfetto/trace/ftrace/generic.proto',
-    'protos/perfetto/trace/ftrace/kmem.proto',
-    'protos/perfetto/trace/ftrace/lowmemorykiller.proto',
-    'protos/perfetto/trace/ftrace/mm_event.proto',
-    'protos/perfetto/trace/ftrace/power.proto',
-    'protos/perfetto/trace/ftrace/raw_syscalls.proto',
-    'protos/perfetto/trace/ftrace/sched.proto',
-    'protos/perfetto/trace/ftrace/signal.proto',
-    'protos/perfetto/trace/ftrace/systrace.proto',
-    'protos/perfetto/trace/ftrace/task.proto',
-    'protos/perfetto/trace/ftrace/vmscan.proto',
-    'protos/perfetto/trace/interned_data/interned_data.proto',
-    'protos/perfetto/trace/perfetto/perfetto_metatrace.proto',
-    'protos/perfetto/trace/power/battery_counters.proto',
-    'protos/perfetto/trace/power/power_rails.proto',
-    'protos/perfetto/trace/profiling/heap_graph.proto',
-    'protos/perfetto/trace/profiling/profile_common.proto',
-    'protos/perfetto/trace/profiling/profile_packet.proto',
-    'protos/perfetto/trace/ps/process_stats.proto',
-    'protos/perfetto/trace/ps/process_tree.proto',
-    'protos/perfetto/trace/sys_stats/sys_stats.proto',
-    'protos/perfetto/trace/system_info.proto',
-    'protos/perfetto/trace/trace.proto',
-    'protos/perfetto/trace/trace_packet.proto',
-    'protos/perfetto/trace/trace_packet_defaults.proto',
-    'protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto',
-    'protos/perfetto/trace/track_event/chrome_histogram_sample.proto',
-    'protos/perfetto/trace/track_event/chrome_keyed_service.proto',
-    'protos/perfetto/trace/track_event/chrome_legacy_ipc.proto',
-    'protos/perfetto/trace/track_event/chrome_process_descriptor.proto',
-    'protos/perfetto/trace/track_event/chrome_thread_descriptor.proto',
-    'protos/perfetto/trace/track_event/chrome_user_event.proto',
-    'protos/perfetto/trace/track_event/debug_annotation.proto',
-    'protos/perfetto/trace/track_event/log_message.proto',
-    'protos/perfetto/trace/track_event/process_descriptor.proto',
-    'protos/perfetto/trace/track_event/source_location.proto',
-    'protos/perfetto/trace/track_event/task_execution.proto',
-    'protos/perfetto/trace/track_event/thread_descriptor.proto',
-    'protos/perfetto/trace/track_event/track_descriptor.proto',
-    'protos/perfetto/trace/track_event/track_event.proto',
-    'protos/perfetto/trace/trigger.proto',
-    'protos/perfetto/trace/gpu/gpu_counter_event.proto',
-    'protos/perfetto/trace/gpu/gpu_log.proto',
-    'protos/perfetto/trace/gpu/gpu_render_stage_event.proto',
-    'protos/perfetto/trace/gpu/vulkan_api_event.proto',
-)
-
+TRACE_PROTO_ROOTS = CONFIG_PROTO_ROOTS + ['protos/perfetto/trace/trace.proto']
 MERGED_TRACE_PROTO = 'protos/perfetto/trace/perfetto_trace.proto'
 
-METRICS_PROTOS = (
-    'protos/perfetto/metrics/metrics.proto',
-)
-
+METRICS_PROTOS_ROOTS = ['protos/perfetto/metrics/metrics.proto']
 MERGED_METRICS_PROTO = 'protos/perfetto/metrics/perfetto_merged_metrics.proto'
 
 REPLACEMENT_HEADER = '''
@@ -141,9 +52,24 @@
 '''
 
 
-def merge_protos_content(proto_paths, follow_imports, added_files, add_header):
+def get_transitive_imports(rel_path, visited):
+  if rel_path in visited:
+    return []
+  visited.add(rel_path)
+  with open(os.path.join(PROJECT_ROOT, rel_path), 'r', encoding='utf-8') as f:
+    content = f.read()
+  imports = re.findall(r'^import "(.*)";\n', content, flags=re.MULTILINE)
+  res = []
+  for child in sorted(imports):
+    res += get_transitive_imports(child, visited)
+  res += [rel_path]
+  return res
+
+
+def merge_protos_content(proto_paths):
   root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
-  merged_content = ''
+  merged_content = REPLACEMENT_HEADER.lstrip() % __file__
+  added_files = set()
   for proto in proto_paths:
     if proto in added_files:
       continue
@@ -157,13 +83,6 @@
     header = re.match(r'\/(\*|\/)(?:.|\s)*?package .*;\n', content)
     header = header.group(0)
     content = content[len(header):]
-    if merged_content == '' and add_header:
-      merged_content += REPLACEMENT_HEADER.lstrip() % __file__
-
-    if follow_imports:
-      matches = re.findall(r'^import "(.*)";\n\n?', content, flags=re.MULTILINE)
-      merged_content += merge_protos_content(
-          matches, follow_imports, added_files, add_header=False)
 
     content = re.sub(r'^import.*?\n\n?', '', content, flags=re.MULTILINE)
     merged_content += '\n// Begin of %s\n' % proto
@@ -174,8 +93,11 @@
   definitions = re.finditer(definitions_re, merged_content, re.MULTILINE)
   types = set((match.group(1) for match in definitions))
 
-  uses_re = r'^( +)(?:repeated)?(?:optional)?\s'\
-            r'?([A-Z]\w+.*)\s+[a-z]\w+\s*=\s*(\d+);'
+  # Limitation: |types| doesn't track the nesting of messages, so a reference to
+  # a nested message (optional One.Two f = 1;) is simplified to its leafmost
+  # name (Two in this example).
+  uses_re = r'^( +)(?:repeated)?(?:optional)?\s?'\
+      r'(?:[A-Z]\w+\.)*([A-Z]\w+)\s+[a-z]\w*\s*=\s*(\d+);'
   uses = re.finditer(uses_re, merged_content, re.MULTILINE)
   substitutions = []
   for use in uses:
@@ -194,12 +116,14 @@
   return merged_content
 
 
-def merge_protos(proto_paths, output_path, follow_imports):
+def merge_protos(root_paths, output_path):
   root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
-  merged_content = merge_protos_content(
-      proto_paths, follow_imports, added_files=set(), add_header=True)
-  out_path = os.path.join(root_dir, output_path)
+  all_protos = []
+  for root_path in root_paths:
+    all_protos += get_transitive_imports(root_path, visited=set())
+  merged_content = merge_protos_content(all_protos)
 
+  out_path = os.path.join(root_dir, output_path)
   prev_content = None
   if os.path.exists(out_path):
     with open(out_path, 'r', encoding='utf-8') as fprev:
@@ -218,11 +142,9 @@
 
 
 def main():
-  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)
+  result = merge_protos(CONFIG_PROTO_ROOTS, MERGED_CONFIG_PROTO)
+  result &= merge_protos(TRACE_PROTO_ROOTS, MERGED_TRACE_PROTO)
+  result &= merge_protos(METRICS_PROTOS_ROOTS, MERGED_METRICS_PROTO)
   return 0 if result else 1
 
 
diff --git a/tools/heap_profile b/tools/heap_profile
index 8662bbf..eaac620 100755
--- a/tools/heap_profile
+++ b/tools/heap_profile
@@ -29,10 +29,11 @@
 import tempfile
 import time
 import urllib
+import uuid
 
 TRACE_TO_TEXT_SHAS = {
-    'linux': 'f9c206e242c49ebd4ec5e4138c61f4fb2816065b',
-    'mac': '7e1803f5c51d0763061d2980fd9af5750ac9d9c8',
+    'linux': '8e7b02e8ce710f8c8f9c7c7d5aa00cc724a7c059',
+    'mac': '332099f1650ec7eaa57069344b5bcf3c23a08839',
 }
 TRACE_TO_TEXT_PATH = tempfile.gettempdir()
 TRACE_TO_TEXT_BASE_URL = ('https://storage.googleapis.com/perfetto/')
@@ -43,6 +44,7 @@
     'stderr': NULL,
 }
 
+UUID = str(uuid.uuid4())
 
 def check_hash(file_name, sha_value):
   with open(file_name, 'rb') as fd:
@@ -78,7 +80,7 @@
 }
 '''
 
-CFG_IDENT = '      '
+CFG_INDENT = '      '
 CFG = '''buffers {{
   size_kb: 32768
 }}
@@ -111,9 +113,11 @@
       }}
 """
 
+PROFILE_DEVICE_PATH = '/data/misc/perfetto-traces/profile-' + UUID
+PROFILE_LOCAL_PATH = '/tmp/profile-' + UUID
+
 PERFETTO_CMD = ('CFG=\'{cfg}\'; echo ${{CFG}} | '
-                'perfetto --txt -c - -o '
-                '/data/misc/perfetto-traces/profile-{user} -d')
+                'perfetto --txt -c - -o ' + PROFILE_DEVICE_PATH + ' -d')
 IS_INTERRUPTED = False
 
 
@@ -219,6 +223,11 @@
       "rather than at the time of the dump.",
       action="store_true")
   parser.add_argument(
+      "--disable-fork-teardown",
+      help="Do not tear down client in forks. This can be useful for programs "
+      "that use vfork. Android 11+ only.",
+      action="store_true")
+  parser.add_argument(
       "--simpleperf",
       action="store_true",
       help="Get simpleperf profile of heapprofd. This is "
@@ -230,6 +239,12 @@
       "--print-config",
       action="store_true",
       help="Print config instead of running. For debugging.")
+  parser.add_argument(
+      "-o",
+      "--output",
+      help="Output directory.",
+      metavar="DIRECTORY",
+      default=None)
 
   args = parser.parse_args()
 
@@ -270,6 +285,8 @@
     target_cfg += "no_running: true\n"
   if args.dump_at_max:
     target_cfg += "dump_at_max: true\n"
+  if args.disable_fork_teardown:
+    target_cfg += "disable_fork_teardown: true\n"
   if args.pid:
     for pid in args.pid.split(','):
       try:
@@ -277,10 +294,10 @@
       except ValueError:
         print("FATAL: invalid PID %s" % pid, file=sys.stderr)
         fail = True
-      target_cfg += '{}pid: {}\n'.format(CFG_IDENT, pid)
+      target_cfg += '{}pid: {}\n'.format(CFG_INDENT, pid)
   if args.name:
     for name in args.name.split(','):
-      target_cfg += '{}process_cmdline: "{}"\n'.format(CFG_IDENT, name)
+      target_cfg += '{}process_cmdline: "{}"\n'.format(CFG_INDENT, name)
 
   if fail:
     parser.print_help()
@@ -332,7 +349,6 @@
       atexit.register(subprocess.check_call,
                       ['adb', 'shell', 'setprop persist.heapprofd.enable 0'])
 
-  user = subprocess.check_output(['adb', 'shell', 'whoami']).strip()
 
   if args.simpleperf:
     subprocess.check_call([
@@ -342,9 +358,25 @@
         '> /dev/null'
     ])
 
+  profile_target = PROFILE_LOCAL_PATH
+  if args.output is not None:
+    profile_target = args.output
+  else:
+    os.mkdir(profile_target)
+
+  if not os.path.isdir(profile_target):
+    print("Output directory {} not found".format(profile_target),
+            file=sys.stderr)
+    return 1
+
+  if os.listdir(profile_target):
+    print("Output directory {} not empty".format(profile_target),
+            file=sys.stderr)
+    return 1
+
   perfetto_pid = subprocess.check_output(
       ['adb', 'exec-out',
-       PERFETTO_CMD.format(cfg=cfg, user=user)]).strip()
+       PERFETTO_CMD.format(cfg=cfg)]).strip()
   try:
     int(perfetto_pid.strip())
   except ValueError:
@@ -383,13 +415,15 @@
     time.sleep(1)
 
   subprocess.check_call([
-      'adb', 'pull', '/data/misc/perfetto-traces/profile-{}'.format(user),
-      '/tmp/profile'
-  ],
-                        stdout=NULL)
+      'adb', 'pull', PROFILE_DEVICE_PATH,
+      os.path.join(profile_target, 'raw-trace')
+  ], stdout=NULL)
+  subprocess.check_call(
+          ['adb', 'shell', 'rm', PROFILE_DEVICE_PATH], stdout=NULL)
 
   trace_to_text_output = subprocess.check_output(
-      [trace_to_text_binary, 'profile', '/tmp/profile'],
+      [trace_to_text_binary, 'profile',
+          os.path.join(profile_target, 'raw-trace')],
       env=os.environ)
   profile_path = None
   for word in trace_to_text_output.split():
@@ -404,30 +438,39 @@
     print_no_profile_error();
     return 1
 
+  for profile_file in profile_files:
+    shutil.copy(os.path.join(profile_path, profile_file), profile_target)
+
   subprocess.check_call(
       ['gzip'] +
-      [os.path.join(profile_path, x) for x in os.listdir(profile_path)])
+      [os.path.join(profile_target, x) for x in profile_files])
 
-  symlink_path = os.path.join(
-      os.path.dirname(profile_path), "heap_profile-latest")
-  if os.path.lexists(symlink_path):
-    os.unlink(symlink_path)
-  os.symlink(profile_path, symlink_path)
-  shutil.copyfile('/tmp/profile', os.path.join(profile_path, 'raw-trace'))
+  symlink_path = None
+  if args.output is None:
+      symlink_path = os.path.join(
+          os.path.dirname(profile_target), "heap_profile-latest")
+      if os.path.lexists(symlink_path):
+        os.unlink(symlink_path)
+      os.symlink(profile_target, symlink_path)
 
   binary_path = os.getenv('PERFETTO_BINARY_PATH')
   if binary_path is not None:
       with open(os.path.join(profile_path, 'symbols'), 'w') as fd:
           ret = subprocess.call([
               trace_to_text_binary, 'symbolize',
-              os.path.join(profile_path, 'raw-trace')],
+              os.path.join(profile_target, 'raw-trace')],
               env=os.environ,
               stdout=fd)
           if ret != 0:
               print("Failed to symbolize. Continuing without symbols.",
                     file=sys.stderr)
 
-  print("Wrote profiles to {} (symlink {})".format(profile_path, symlink_path))
+  if symlink_path is not None:
+    print("Wrote profiles to {} (symlink {})".format(
+        profile_target, symlink_path))
+  else:
+    print("Wrote profiles to {}".format(profile_target))
+
   print("These can be viewed using pprof. Googlers: head to pprof/ and "
         "upload them.")
 
diff --git a/tools/install-build-deps b/tools/install-build-deps
index 1ed1fab..f8f0ff0 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',
-     '860e8682e27eea30cc604b60c9cab83d0b047012', '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-20200122-100845.zip',
-        '56ac45b5239fda50d33cf05bfd329dcb3efb0b2a',
+        'https://storage.googleapis.com/perfetto/test-data-20200331-130927.zip',
+        '80632f754293e47e1cfa1ff6bcf15d11d589a126',
         'all',
     ),
 
@@ -193,7 +193,7 @@
 ]
 
 # This variable is updated by tools/roll-catapult-trace-viewer.
-CATAPULT_SHA1 = 'ff5d8fd7244680b4d4456c25d5fdc04c76f9ef66'
+CATAPULT_SHA1 = '4d292db280e546ac8ab35acf249214e0979ab781'
 
 TYPEFACES_SHA1 = '4fb455de506f8a2859dc5264b8448c2559b08ab8'
 
@@ -228,6 +228,8 @@
 ]
 
 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+UI_DIR = os.path.join(ROOT_DIR, 'ui')
+NODE_MODULES_STATUS_FILE = os.path.join(UI_DIR, 'node_modules', '.last_install')
 
 
 def ReadFile(path):
@@ -266,9 +268,11 @@
   return ReadFile(os.path.join(path, '.git', 'HEAD')) == revision
 
 
-def CheckoutGitRepo(path, git_url, revision):
+def CheckoutGitRepo(path, git_url, revision, check_only):
   if IsGitRepoCheckoutOutAtRevision(path, revision):
     return False
+  if check_only:
+    return True
   if os.path.exists(path):
     shutil.rmtree(path)
   MkdirRecursive(path)
@@ -282,10 +286,25 @@
 
 
 def InstallNodeModules():
-  ui_dir = os.path.join(ROOT_DIR, 'ui')
-  logging.info("Running npm install in {0}".format(ui_dir))
-  subprocess.check_call([os.path.join(ui_dir, 'npm'), 'install', '--no-save'],
-                        cwd=os.path.join(ROOT_DIR, 'ui'))
+  logging.info("Running npm install in {0}".format(UI_DIR))
+  subprocess.check_call([os.path.join(UI_DIR, 'npm'), 'install', '--no-save'],
+                        cwd=UI_DIR)
+  with open(NODE_MODULES_STATUS_FILE, 'w') as f:
+    f.write(HashLocalFile(os.path.join(UI_DIR, 'package-lock.json')))
+
+
+def CheckNodeModules():
+  """Returns True if the modules are up-to-date.
+
+  There doesn't seem to be an easy way to check node modules versions. Instead
+  just check if package-lock.json changed since the last `npm install` call.
+  """
+  if not os.path.exists(NODE_MODULES_STATUS_FILE):
+    return False
+  with open(NODE_MODULES_STATUS_FILE, 'r') as f:
+    actual = f.read()
+  expected = HashLocalFile(os.path.join(UI_DIR, 'package-lock.json'))
+  return expected == actual
 
 
 def CheckHashes():
@@ -306,16 +325,16 @@
 
 def Main():
   parser = argparse.ArgumentParser()
-  parser.add_argument('--no-android', action='store_true')
+  parser.add_argument('--android', action='store_true')
   parser.add_argument('--ui', action='store_true')
-  parser.add_argument(
-      '--check-hashes', help='Check hashes for all URLs', action='store_true')
+  parser.add_argument('--check-only')
+  parser.add_argument('--verify', help='Check all URLs', action='store_true')
   args = parser.parse_args()
-  if args.check_hashes:
+  if args.verify:
     CheckHashes()
     return 0
   deps = BUILD_DEPS_HOST
-  if not args.no_android:
+  if args.android:
     deps += BUILD_DEPS_ANDROID + TEST_DEPS_ANDROID
   if args.ui:
     deps += UI_DEPS
@@ -325,7 +344,8 @@
       continue
     local_path = os.path.join(ROOT_DIR, rel_path)
     if url.endswith('.git'):
-      deps_updated |= CheckoutGitRepo(local_path, url, expected_sha1)
+      deps_updated |= CheckoutGitRepo(local_path, url, expected_sha1,
+                                      args.check_only)
       continue
     is_zip = local_path.endswith('.zip') or local_path.endswith('.tgz')
     zip_target_dir = local_path[:-4] if is_zip else None
@@ -335,6 +355,8 @@
         (is_zip and ReadFile(zip_dir_stamp) == expected_sha1)):
       continue
     deps_updated = True
+    if args.check_only:
+      continue
     MkdirRecursive(os.path.dirname(rel_path))
     if HashLocalFile(local_path) != expected_sha1:
       download_path = local_path + '.tmp'
@@ -383,7 +405,20 @@
 
   if args.ui:
     # Needs to happen after nodejs is installed above.
-    InstallNodeModules()
+    if args.check_only:
+      deps_updated = not CheckNodeModules()
+    else:
+      InstallNodeModules()
+
+  if args.check_only:
+    if not deps_updated:
+      with open(args.check_only, 'w') as f:
+        f.write('OK')  # The content is irrelevant, just keep GN happy.
+      return 0
+    argz = ' '.join([x for x in sys.argv[1:] if not '--check-only' in x])
+    sys.stderr.write('\033[91mBuild deps are stale. ' +
+                     'Please run tools/install-build-deps %s\033[0m' % argz)
+    return 1
 
   if deps_updated:
     # Stale binary files may be compiled against old sysroot headers that aren't
diff --git a/tools/java_heap_dump b/tools/java_heap_dump
new file mode 100755
index 0000000..f84a594
--- /dev/null
+++ b/tools/java_heap_dump
@@ -0,0 +1,180 @@
+#!/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.
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import argparse
+import os
+import subprocess
+import sys
+import tempfile
+import time
+
+NULL = open(os.devnull)
+
+PACKAGES_LIST_CFG = '''data_sources {
+  config {
+    name: "android.packages_list"
+  }
+}
+'''
+
+CFG_IDENT = '      '
+CFG = '''buffers {{
+  size_kb: 100024
+  fill_policy: RING_BUFFER
+}}
+
+data_sources {{
+  config {{
+    name: "android.java_hprof"
+    java_hprof_config {{
+{target_cfg}
+{continuous_dump_config}
+    }}
+  }}
+}}
+
+duration_ms: 20000
+'''
+
+CONTINUOUS_DUMP = """
+      continuous_dump_config {{
+        dump_phase_ms: 0
+        dump_interval_ms: {dump_interval}
+      }}
+"""
+
+PERFETTO_CMD = ('CFG=\'{cfg}\'; echo ${{CFG}} | '
+                'perfetto --txt -c - -o '
+                '/data/misc/perfetto-traces/java-profile-{user} -d')
+
+def main(argv):
+  parser = argparse.ArgumentParser()
+  parser.add_argument(
+      "-o",
+      "--output",
+      help="Filename to save profile to.",
+      metavar="FILE",
+      default=None)
+  parser.add_argument(
+      "-p",
+      "--pid",
+      help="Comma-separated list of PIDs to "
+      "profile.",
+      metavar="PIDS")
+  parser.add_argument(
+      "-n",
+      "--name",
+      help="Comma-separated list of process "
+      "names to profile.",
+      metavar="NAMES")
+  parser.add_argument(
+      "-c",
+      "--continuous-dump",
+      help="Dump interval in ms. 0 to disable continuous dump.",
+      type=int,
+      default=0)
+  parser.add_argument(
+      "--no-versions",
+      action="store_true",
+      help="Do not get version information about APKs.")
+  parser.add_argument(
+      "--dump-smaps",
+      action="store_true",
+      help="Get information about /proc/$PID/smaps of target.")
+  parser.add_argument(
+      "--print-config",
+      action="store_true",
+      help="Print config instead of running. For debugging.")
+
+  args = parser.parse_args()
+
+  fail = False
+  if args.pid is None and args.name is None:
+    print("FATAL: Neither PID nor NAME given.", file=sys.stderr)
+    fail = True
+
+  target_cfg = ""
+  if args.pid:
+    for pid in args.pid.split(','):
+      try:
+        pid = int(pid)
+      except ValueError:
+        print("FATAL: invalid PID %s" % pid, file=sys.stderr)
+        fail = True
+      target_cfg += '{}pid: {}\n'.format(CFG_IDENT, pid)
+  if args.name:
+    for name in args.name.split(','):
+      target_cfg += '{}process_cmdline: "{}"\n'.format(CFG_IDENT, name)
+  if args.dump_smaps:
+    target_cfg += '{}dump_smaps: true\n'.format(CFG_IDENT)
+
+  if fail:
+    parser.print_help()
+    return 1
+
+  output_file = args.output
+  if output_file is None:
+    fd, name = tempfile.mkstemp('profile')
+    os.close(fd)
+    output_file = name
+
+  continuous_dump_cfg = ""
+  if args.continuous_dump:
+    continuous_dump_cfg = CONTINUOUS_DUMP.format(
+        dump_interval=args.continuous_dump)
+  cfg = CFG.format(
+      target_cfg=target_cfg,
+      continuous_dump_config=continuous_dump_cfg)
+  if not args.no_versions:
+    cfg += PACKAGES_LIST_CFG
+
+  if args.print_config:
+    print(cfg)
+    return 0
+
+  user = subprocess.check_output(['adb', 'shell', 'whoami']).strip()
+  perfetto_pid = subprocess.check_output(
+      ['adb', 'exec-out',
+       PERFETTO_CMD.format(cfg=cfg, user=user)]).strip()
+  try:
+    int(perfetto_pid.strip())
+  except ValueError:
+    print("Failed to invoke perfetto: {}".format(perfetto_pid), file=sys.stderr)
+    return 1
+
+  print("Dumping Java Heap.")
+  exists = True
+
+  # Wait for perfetto cmd to return.
+  while exists:
+    exists = subprocess.call(
+        ['adb', 'shell', '[ -d /proc/{} ]'.format(perfetto_pid)]) == 0
+    time.sleep(1)
+
+  subprocess.check_call(
+    ['adb', 'pull', '/data/misc/perfetto-traces/java-profile-{}'.format(user),
+     output_file], stdout=NULL)
+
+  print("Wrote profile to {}".format(output_file))
+  print("This can be viewed using https://ui.perfetto.dev.")
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv))
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/roll-catapult-trace-viewer b/tools/roll-catapult-trace-viewer
index a8234b0..c163885 100755
--- a/tools/roll-catapult-trace-viewer
+++ b/tools/roll-catapult-trace-viewer
@@ -42,11 +42,30 @@
 
 (
   cd "$OUTDIR"
+  cat >extra_origin_trials <<EOF
+  <!-- WebComponents V0 origin trial token for https://*.ui.perfetto.dev
+  Expires 1 Feb 2021. https://crbug.com/1021137. -->
+  <meta http-equiv="origin-trial" content="AjGFDFU57Af4e5OJJQd7kmYR0nEiObDCHkev6BBWzhGohACl1ri+pMhaVe9V8dDBaXDkWy4g7WYj3c5GiPwatgIAAABreyJvcmlnaW4iOiJodHRwczovL3VpLnBlcmZldHRvLmRldjo0NDMiLCJmZWF0dXJlIjoiV2ViQ29tcG9uZW50c1YwIiwiZXhwaXJ5IjoxNjEyMjIzOTk5LCJpc1N1YmRvbWFpbiI6dHJ1ZX0=">
+
+  <!-- WebComponents V0 origin trial token for http://localhost:10000
+  Expires 28 Jan 2021. https://crbug.com/1021137. -->
+  <meta http-equiv="origin-trial" content="AicMEv5glMGL1lq6ZRsxFJj8xlhn3XDYZrHK0/2KreAD/r62vTFjUBOueeMTxWuU1IlRXqCugRFDD7rY45YEgwkAAABTeyJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjEwMDAwIiwiZmVhdHVyZSI6IldlYkNvbXBvbmVudHNWMCIsImV4cGlyeSI6MTYxMTg0MDczNH0=">
+
+  <!-- WebComponents V0 origin trial token for https://staging-dot-perfetto-ui.appspot.com
+  Expires 1 Feb 2021. https://crbug.com/1021137. -->
+  <meta http-equiv="origin-trial" content="Au1cwnWfBB/GCD22HnNZE93/KamhGDsz8BZbEewICJB2PRtW+E1bobrtZbTZs8q5748uRiKXPvgaut5JOZ8jSw4AAABseyJvcmlnaW4iOiJodHRwczovL3N0YWdpbmctZG90LXBlcmZldHRvLXVpLmFwcHNwb3QuY29tOjQ0MyIsImZlYXR1cmUiOiJXZWJDb21wb25lbnRzVjAiLCJleHBpcnkiOjE2MTIyMjM5OTl9">
+
+  <!-- WebComponents V0 origin trial token for https://storage.googleapis.com/
+  Expires 1 Feb 2021. https://crbug.com/1021137. -->
+  <meta http-equiv="origin-trial" content="AtobKUpdVFIb6cx2Ev0EbAFX4SzLuXPnsnADRA8JV5w4B64q65gz42shquyLLNd2QP9rY22oNGxbatpTO0kd2AIAAABfeyJvcmlnaW4iOiJodHRwczovL3N0b3JhZ2UuZ29vZ2xlYXBpcy5jb206NDQzIiwiZmVhdHVyZSI6IldlYkNvbXBvbmVudHNWMCIsImV4cGlyeSI6MTYxMjIyMzk5OX0=">
+EOF
+
   mv about_tracing.html catapult_trace_viewer.html
   mv about_tracing.js catapult_trace_viewer.js
-  sed -i '' -e \
-      's|src="tracing.js"|src="/assets/catapult_trace_viewer.js"|g' \
+  sed -i -e \
+      's|src="tracing.js"|src="catapult_trace_viewer.js"|g' \
       catapult_trace_viewer.html
+   sed -i -e '/<head/r extra_origin_trials' catapult_trace_viewer.html
   tar -zcf "$ARCHIVE" catapult_trace_viewer.{js,html}
 )
 
@@ -57,8 +76,8 @@
 rm -rf "$OUTDIR"
 
 # Update the reference to the new prebuilt in tools/install-build-deps.
-sed -i '' -e \
+sed -i -e \
     "s/^CATAPULT_SHA1 =.*/CATAPULT_SHA1 = '"$SHA1"'/g" \
      "$PROJECT_ROOT/tools/install-build-deps"
 
-"$PROJECT_ROOT/tools/install-build-deps" --ui --no-android
\ No newline at end of file
+"$PROJECT_ROOT/tools/install-build-deps" --ui
\ No newline at end of file
diff --git a/tools/run_android_test b/tools/run_android_test
index 27fd273..4707796 100755
--- a/tools/run_android_test
+++ b/tools/run_android_test
@@ -90,6 +90,8 @@
 
 
 def AdbPush(host, device):
+  if not os.path.exists(host):
+    logging.fatal('Cannot find %s. Was it built?', host)
   cmd = [ADB_PATH, 'push', host, device]
   print '> adb push ' + ' '.join(cmd[2:])
   with open(os.devnull) as devnull:
@@ -154,6 +156,10 @@
   # See https://android.googlesource.com/platform/system/core/+/master/rootdir/etc/ld.config.txt.
   AdbPush(test_bin, "/data/nativetest")
 
+  # These two binaries are required to run perfetto_integrationtests.
+  AdbPush(os.path.join(args.out_dir, "perfetto"), "/data/nativetest")
+  AdbPush(os.path.join(args.out_dir, "trigger_perfetto"), "/data/nativetest")
+
   if not args.no_data_deps:
     for dep in EnumerateDataDeps():
       AdbPush(os.path.join(ROOT_DIR, dep), target_dir + '/' + dep)
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 087c2ad..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/strip_android_host_binary.py b/tools/strip_android_host_binary.py
new file mode 100755
index 0000000..488368a
--- /dev/null
+++ b/tools/strip_android_host_binary.py
@@ -0,0 +1,59 @@
+#!/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.
+
+from __future__ import print_function
+
+import argparse
+import os
+import subprocess
+import sys
+
+THIS_DIR = os.path.realpath(os.path.dirname(__file__))
+
+
+def android_build_top():
+  return os.path.realpath(os.path.join(THIS_DIR, '../../..'))
+
+
+def clang_build():
+  gofile = os.path.join(android_build_top(), 'build', 'soong', 'cc', 'config',
+                        'global.go')
+  try:
+    with open(gofile) as f:
+      lines = f.readlines()
+      versionLine = [l for l in lines if 'ClangDefaultVersion' in l][0]
+      start, end = versionLine.index('"'), versionLine.rindex('"')
+      return versionLine[start + 1:end]
+  except Exception as err:
+    raise RuntimeError("Extracting Clang version failed with {0}".format(err))
+
+
+def llvm_strip():
+  return os.path.join(android_build_top(), 'prebuilts', 'clang', 'host',
+                      'linux-x86', clang_build(), 'bin', 'llvm-strip')
+
+
+def main():
+  parser = argparse.ArgumentParser(
+      description='Strips a binary in the Android tree.')
+  parser.add_argument(
+      '-o', dest='output', default=None, help='Output file', required=True)
+  parser.add_argument('binary', type=str, help='location of binary')
+  args = parser.parse_args()
+  return subprocess.call([llvm_strip(), args.binary, '-o', args.output])
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/tools/trace_processor b/tools/trace_processor
index 709eb72..6476d14 100755
--- a/tools/trace_processor
+++ b/tools/trace_processor
@@ -31,8 +31,8 @@
 import urllib
 
 TRACE_PROCESSOR_SHELL_SHAS = {
-    'linux': '2c3016d83ac75fe1a654680b793e82f03a31debe',
-    'mac': '693a9bf28d4116113e63d114cf9cf6dbab2b1874',
+    'linux': '7532ce79b260993018fdd515fdf99d3151ce48d4',
+    'mac': 'ed29b0eefe4f209504c18400b844cb00b4bc323b',
 }
 TRACE_PROCESSOR_SHELL_PATH = tempfile.gettempdir()
 TRACE_PROCESSOR_SHELL_BASE_URL = ('https://storage.googleapis.com/perfetto/')
diff --git a/tools/trace_to_text/BUILD.gn b/tools/trace_to_text/BUILD.gn
index 6892d1a..bb0cf7b 100644
--- a/tools/trace_to_text/BUILD.gn
+++ b/tools/trace_to_text/BUILD.gn
@@ -63,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",
@@ -78,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
@@ -133,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.
diff --git a/tools/trace_to_text/main.cc b/tools/trace_to_text/main.cc
index 8d1e811..e3c084c 100644
--- a/tools/trace_to_text/main.cc
+++ b/tools/trace_to_text/main.cc
@@ -155,11 +155,11 @@
                        truncate_keep, full_sort);
 
   if (format == "systrace")
-    return TraceToSystrace(input_stream, output_stream, /*compress=*/false,
+    return TraceToSystrace(input_stream, output_stream, /*ctrace=*/false,
                            truncate_keep, full_sort);
 
   if (format == "ctrace")
-    return TraceToSystrace(input_stream, output_stream, /*compress=*/true,
+    return TraceToSystrace(input_stream, output_stream, /*ctrace=*/true,
                            truncate_keep, full_sort);
 
   if (truncate_keep != Keep::kAll) {
diff --git a/tools/trace_to_text/pprof_builder.cc b/tools/trace_to_text/pprof_builder.cc
index 7d72434..54a1527 100644
--- a/tools/trace_to_text/pprof_builder.cc
+++ b/tools/trace_to_text/pprof_builder.cc
@@ -97,7 +97,7 @@
     // table.
     return 0;
   }
-  return it.Get(0).long_value;
+  return it.Get(0).AsLong();
 }
 
 bool VerifyPIDStats(trace_processor::TraceProcessor* tp, uint64_t pid) {
@@ -150,21 +150,22 @@
                             count_it.Status().message().c_str());
     return {};
   }
-  int64_t count = count_it.Get(0).long_value;
+  int64_t count = count_it.Get(0).AsLong();
 
   Iterator it = tp->ExecuteQuery(
       "select id, parent_id, frame_id from stack_profile_callsite order by "
       "depth;");
   std::vector<std::vector<int64_t>> result(static_cast<size_t>(count));
   while (it.Next()) {
-    int64_t id = it.Get(0).long_value;
-    int64_t parent_id = it.Get(1).long_value;
-    int64_t frame_id = it.Get(2).long_value;
+    int64_t id = it.Get(0).AsLong();
+    int64_t frame_id = it.Get(2).AsLong();
     std::vector<int64_t>& path = result[static_cast<size_t>(id)];
     path.push_back(frame_id);
-    if (parent_id != -1) {
+
+    auto parent_id_value = it.Get(1);
+    if (!parent_id_value.is_null()) {
       const std::vector<int64_t>& parent_path =
-          result[static_cast<size_t>(parent_id)];
+          result[static_cast<size_t>(parent_id_value.AsLong())];
       path.insert(path.end(), parent_path.begin(), parent_path.end());
     }
   }
@@ -188,9 +189,9 @@
   Iterator it = tp->ExecuteQuery(
       "SELECT symbol_set_id, id, line_number FROM stack_profile_symbol;");
   while (it.Next()) {
-    int64_t symbol_set_id = it.Get(0).long_value;
-    int64_t id = it.Get(1).long_value;
-    int64_t line_number = it.Get(2).long_value;
+    int64_t symbol_set_id = it.Get(0).AsLong();
+    int64_t id = it.Get(1).AsLong();
+    int64_t line_number = it.Get(2).AsLong();
     result[symbol_set_id].emplace_back(
         Line{id, static_cast<uint32_t>(line_number)});
   }
@@ -214,7 +215,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,
@@ -263,7 +265,7 @@
       auto* gsample = result_->add_sample();
       protozero::PackedVarInt sample_values;
       for (size_t i = 0; i < base::ArraySize(kViews); ++i) {
-        int64_t callstack_id = (*view_its)[i].Get(0).long_value;
+        int64_t callstack_id = (*view_its)[i].Get(0).AsLong();
         if (i == 0) {
           auto frames = FramesForCallstack(callstack_id);
           if (frames.empty())
@@ -274,12 +276,12 @@
           gsample->set_location_id(location_ids);
           seen_frames->insert(frames.cbegin(), frames.cend());
         } else {
-          if (callstack_id != (*view_its)[i].Get(0).long_value) {
+          if (callstack_id != (*view_its)[i].Get(0).AsLong()) {
             PERFETTO_DFATAL_OR_ELOG("Wrong callstack.");
             return false;
           }
         }
-        sample_values.Append((*view_its)[i].Get(1).long_value);
+        sample_values.Append((*view_its)[i].Get(1).AsLong());
       }
       gsample->set_value(sample_values);
     }
@@ -293,21 +295,21 @@
         "FROM stack_profile_mapping;");
     size_t mappings_no = 0;
     while (mapping_it.Next()) {
-      int64_t id = mapping_it.Get(0).long_value;
+      int64_t id = mapping_it.Get(0).AsLong();
       if (seen_mappings.find(id) == seen_mappings.end())
         continue;
       ++mappings_no;
-      auto interned_filename = Intern(mapping_it.Get(4).string_value);
+      auto interned_filename = Intern(mapping_it.Get(4).AsString());
       auto* gmapping = result_->add_mapping();
       gmapping->set_id(ToPprofId(id));
       // Do not set the build_id here to avoid downstream services
       // trying to symbolize (e.g. b/141735056)
       gmapping->set_file_offset(
-          static_cast<uint64_t>(mapping_it.Get(1).long_value));
+          static_cast<uint64_t>(mapping_it.Get(1).AsLong()));
       gmapping->set_memory_start(
-          static_cast<uint64_t>(mapping_it.Get(2).long_value));
+          static_cast<uint64_t>(mapping_it.Get(2).AsLong()));
       gmapping->set_memory_limit(
-          static_cast<uint64_t>(mapping_it.Get(3).long_value));
+          static_cast<uint64_t>(mapping_it.Get(3).AsLong()));
       gmapping->set_filename(interned_filename);
     }
     if (!mapping_it.Status().ok()) {
@@ -328,17 +330,17 @@
         "SELECT id, name, source_file FROM stack_profile_symbol");
     size_t symbols_no = 0;
     while (symbol_it.Next()) {
-      int64_t id = symbol_it.Get(0).long_value;
+      int64_t id = symbol_it.Get(0).AsLong();
       if (seen_symbol_ids.find(id) == seen_symbol_ids.end())
         continue;
       ++symbols_no;
-      const std::string& name = symbol_it.Get(1).string_value;
+      const std::string& name = symbol_it.Get(1).AsString();
       std::string demangled_name = name;
       MaybeDemangle(&demangled_name);
 
       auto interned_demangled_name = Intern(demangled_name);
       auto interned_system_name = Intern(name);
-      auto interned_filename = Intern(symbol_it.Get(2).string_value);
+      auto interned_filename = Intern(symbol_it.Get(2).AsString());
       auto* gfunction = result_->add_function();
       gfunction->set_id(ToPprofId(id));
       gfunction->set_name(interned_demangled_name);
@@ -368,16 +370,16 @@
         "FROM stack_profile_frame spf;");
     size_t frames_no = 0;
     while (frame_it.Next()) {
-      int64_t frame_id = frame_it.Get(0).long_value;
+      int64_t frame_id = frame_it.Get(0).AsLong();
       if (seen_frames.find(frame_id) == seen_frames.end())
         continue;
       frames_no++;
-      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;
+      std::string frame_name = frame_it.Get(1).AsString();
+      int64_t mapping_id = frame_it.Get(2).AsLong();
+      int64_t rel_pc = frame_it.Get(3).AsLong();
       base::Optional<int64_t> symbol_set_id;
       if (!frame_it.Get(4).is_null())
-        symbol_set_id = frame_it.Get(4).long_value;
+        symbol_set_id = frame_it.Get(4).AsLong();
 
       seen_mappings->emplace(mapping_id);
       auto* glocation = result_->add_location();
@@ -543,7 +545,7 @@
     return false;
   }
 
-  int64_t max_symbol_id = max_symbol_id_it.Get(0).long_value;
+  int64_t max_symbol_id = max_symbol_id_it.Get(0).AsLong();
   const auto callsite_to_frames = GetCallsiteToFrames(tp);
   const auto symbol_set_id_to_lines = GetSymbolSetIdToLines(tp);
 
@@ -552,9 +554,9 @@
   while (it.Next()) {
     GProfileBuilder builder(callsite_to_frames, symbol_set_id_to_lines,
                             max_symbol_id);
-    uint64_t upid = static_cast<uint64_t>(it.Get(0).long_value);
-    uint64_t ts = static_cast<uint64_t>(it.Get(1).long_value);
-    uint64_t profile_pid = static_cast<uint64_t>(it.Get(2).long_value);
+    uint64_t upid = static_cast<uint64_t>(it.Get(0).AsLong());
+    uint64_t ts = static_cast<uint64_t>(it.Get(1).AsLong());
+    uint64_t profile_pid = static_cast<uint64_t>(it.Get(2).AsLong());
     if ((pid > 0 && profile_pid != pid) ||
         (!timestamps.empty() && std::find(timestamps.begin(), timestamps.end(),
                                           ts) == timestamps.end())) {
@@ -571,7 +573,7 @@
 
     std::string profile_proto = builder.GenerateGProfile(tp, upid, ts);
     output->emplace_back(SerializedProfile{
-        static_cast<uint64_t>(pid_it.Get(0).long_value), profile_proto});
+        static_cast<uint64_t>(pid_it.Get(0).AsLong()), profile_proto});
   }
   if (any_fail) {
     PERFETTO_ELOG(
diff --git a/tools/trace_to_text/trace_to_systrace.cc b/tools/trace_to_text/trace_to_systrace.cc
index 8e8b310..e5900c8 100644
--- a/tools/trace_to_text/trace_to_systrace.cc
+++ b/tools/trace_to_text/trace_to_systrace.cc
@@ -164,11 +164,11 @@
 
 int TraceToSystrace(std::istream* input,
                     std::ostream* output,
-                    bool compress,
+                    bool ctrace,
                     Keep truncate_keep,
                     bool full_sort) {
   std::unique_ptr<TraceWriter> trace_writer(
-      compress ? new DeflateTraceWriter(output) : new TraceWriter(output));
+      ctrace ? new DeflateTraceWriter(output) : new TraceWriter(output));
 
   trace_processor::Config config;
   config.force_full_sort = full_sort;
@@ -179,7 +179,9 @@
     return 1;
   tp->NotifyEndOfFile();
 
-  *output << "TRACE:\n";
+  if (ctrace)
+    *output << "TRACE:\n";
+
   return ExtractSystrace(tp.get(), trace_writer.get(),
                          /*wrapped_in_json=*/false, truncate_keep);
 }
diff --git a/tools/trace_to_text/trace_to_systrace.h b/tools/trace_to_text/trace_to_systrace.h
index 4e540f6..06a1505 100644
--- a/tools/trace_to_text/trace_to_systrace.h
+++ b/tools/trace_to_text/trace_to_systrace.h
@@ -33,7 +33,7 @@
 
 int TraceToSystrace(std::istream* input,
                     std::ostream* output,
-                    bool compress,
+                    bool ctrace,
                     Keep truncate_keep,
                     bool full_sort);
 
diff --git a/tools/trace_to_text/utils.cc b/tools/trace_to_text/utils.cc
index e655b38..64a4aee 100644
--- a/tools/trace_to_text/utils.cc
+++ b/tools/trace_to_text/utils.cc
@@ -191,7 +191,12 @@
   // can support multiple dumps in the same trace.
   auto* proto_mapping = packet->set_deobfuscation_mapping();
   for (const auto& p : classes) {
-    const std::string& obfuscated_class_name = p.first;
+    std::string obfuscated_class_name = p.first;
+    while (obfuscated_class_name.size() > 2 &&
+           obfuscated_class_name.substr(obfuscated_class_name.size() - 2) ==
+               "[]") {
+      obfuscated_class_name.resize(obfuscated_class_name.size() - 2);
+    }
     const std::set<std::string>& obfuscated_field_names = p.second;
     auto it = mapping.find(obfuscated_class_name);
     if (it == mapping.end()) {
@@ -221,7 +226,7 @@
 
 TraceWriter::~TraceWriter() = default;
 
-void TraceWriter::Write(std::string s) {
+void TraceWriter::Write(const std::string& s) {
   Write(s.data(), s.size());
 }
 
@@ -242,9 +247,15 @@
 }
 
 DeflateTraceWriter::~DeflateTraceWriter() {
+  // Drain compressor until it has no more input, and has flushed its internal
+  // buffers.
   while (deflate(&stream_, Z_FINISH) != Z_STREAM_END) {
     Flush();
   }
+  // Flush any outstanding output bytes to the backing TraceWriter.
+  Flush();
+  PERFETTO_CHECK(stream_.avail_out == static_cast<size_t>(end_ - start_));
+
   CheckEq(deflateEnd(&stream_), Z_OK);
 }
 
diff --git a/tools/trace_to_text/utils.h b/tools/trace_to_text/utils.h
index d708e04f..e027ad1 100644
--- a/tools/trace_to_text/utils.h
+++ b/tools/trace_to_text/utils.h
@@ -78,7 +78,7 @@
   TraceWriter(std::ostream* output);
   virtual ~TraceWriter();
 
-  void Write(std::string s);
+  void Write(const std::string& s);
   virtual void Write(const char* data, size_t sz);
 
  private:
diff --git a/tools/traceconv b/tools/traceconv
index 5626994..6adbade 100755
--- a/tools/traceconv
+++ b/tools/traceconv
@@ -33,8 +33,8 @@
 # Keep this in sync with the SHAs in catapult file
 # systrace/systrace/tracing_agents/atrace_from_file_agent.py.
 TRACE_TO_TEXT_SHAS = {
-    'linux': 'fe85e328de11f94c038b006201bbd8226bf17432',
-    'mac': '681ae1ea8c54fed7949d0ffa663bb3663ed5db05',
+    'linux': '33d496605b3c861f98e8467ad5a5d974aa0720ea',
+    'mac': 'ecc0f5862fcde5e458d6edf3f3d1b03b609db091',
 }
 TRACE_TO_TEXT_PATH = tempfile.gettempdir()
 TRACE_TO_TEXT_BASE_URL = ('https://storage.googleapis.com/perfetto/')
diff --git a/traced_perf.rc b/traced_perf.rc
new file mode 100644
index 0000000..692977c
--- /dev/null
+++ b/traced_perf.rc
@@ -0,0 +1,47 @@
+# 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.
+
+# Notes:
+# * socket used for receiving /proc/pid/{maps,mem} file descriptors
+# * readproc group to stat(/proc/pid) to find out UID of processes, and read
+#   /proc/pid/cmdline.
+# * KILL capability for sending BIONIC_SIGNAL_PROFILER.
+# * DAC_READ_SEARCH capability for stack unwinding and on-device symbolization (requires
+#   opening libraries/executables for sections not already mapped in).
+# * foreground task group as unwinding based on minidebug info is a heavyweight action.
+service traced_perf /system/bin/traced_perf
+    class late_start
+    disabled
+    socket traced_perf stream 0666 root root
+    user nobody
+    group nobody readproc
+    capabilities KILL DAC_READ_SEARCH
+    writepid /dev/cpuset/foreground/tasks
+
+# Daemon run state:
+# * initially off
+# * |persist.traced_perf.enable| forces daemon to run unconditionally
+# * if kernel doesn't have perf_event_open LSM hooks, daemon is stopped
+# * otherwise, follow |traced.lazy.traced_perf| as an on-demand service
+on property:persist.traced_perf.enable=1
+    start traced_perf
+on property:persist.traced_perf.enable="" && property:sys.init.perf_lsm_hooks=""
+    stop traced_perf
+on property:persist.traced_perf.enable="" && property:sys.init.perf_lsm_hooks=1 && property:traced.lazy.traced_perf=1
+    start traced_perf
+on property:persist.traced_perf.enable="" && property:sys.init.perf_lsm_hooks=1 && property:traced.lazy.traced_perf=""
+    stop traced_perf
+
+on property:persist.traced_perf.enable=0
+    setprop persist.traced_perf.enable ""
diff --git a/ui/BUILD.gn b/ui/BUILD.gn
index d7d67a8..00cb788 100644
--- a/ui/BUILD.gn
+++ b/ui/BUILD.gn
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 import("../gn/perfetto.gni")
+import("../gn/perfetto_check_build_deps.gni")
 import("../gn/wasm.gni")
 import("../protos/perfetto/trace_processor/proto_files.gni")
 
@@ -85,9 +86,7 @@
                              "outputs",
                              "depfile",
                            ])
-    deps = [
-      ":node_modules",
-    ]
+    deps = [ ":node_modules" ]
     if (defined(invoker.deps)) {
       deps += invoker.deps
     }
@@ -116,9 +115,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",
@@ -172,49 +169,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",
-  ]
+  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"
 }
@@ -228,13 +213,14 @@
     inputs += [ "../protos/perfetto/trace_processor/$proto.proto" ]
   }
   inputs += [
+    "../protos/perfetto/common/trace_stats.proto",
+    "../protos/perfetto/common/tracing_service_capabilities.proto",
     "../protos/perfetto/config/perfetto_config.proto",
     "../protos/perfetto/ipc/consumer_port.proto",
     "../protos/perfetto/ipc/wire_protocol.proto",
+    "../protos/perfetto/metrics/metrics.proto",
   ]
-  outputs = [
-    "$ui_gen_dir/protos.js",
-  ]
+  outputs = [ "$ui_gen_dir/protos.js" ]
   node_cmd = "pbjs"
   args = [
            "-t",
@@ -251,15 +237,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",
@@ -281,9 +261,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",
@@ -323,9 +301,7 @@
     "tsconfig.json",
     "src/service_worker/service_worker.ts",
   ]
-  outputs = [
-    "$target_out_dir/service_worker/service_worker.js",
-  ]
+  outputs = [ "$target_out_dir/service_worker/service_worker.js" ]
 
   node_cmd = "tsc"
   args = [
@@ -353,13 +329,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 = [
@@ -373,12 +345,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") {
@@ -391,25 +359,19 @@
     "../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") {
   sources = [
-              "src/assets/heap_profiler.png",
+              "src/assets/brand.png",
+              "src/assets/favicon.png",
               "src/assets/logo-3d.png",
-              "src/assets/logo.png",
               "src/assets/rec_atrace.png",
               "src/assets/rec_battery_counters.png",
               "src/assets/rec_board_voltage.png",
@@ -419,75 +381,60 @@
               "src/assets/rec_cpu_voltage.png",
               "src/assets/rec_cpu_wakeup.png",
               "src/assets/rec_ftrace.png",
+              "src/assets/rec_java_heap_dump.png",
               "src/assets/rec_lmk.png",
               "src/assets/rec_logcat.png",
               "src/assets/rec_long_trace.png",
               "src/assets/rec_mem_hifreq.png",
               "src/assets/rec_meminfo.png",
+              "src/assets/rec_native_heap_profiler.png",
               "src/assets/rec_one_shot.png",
               "src/assets/rec_ps_stats.png",
               "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 = [
     "src/assets/logo-128.png",
-    "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",
-  ]
+  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"
 }
@@ -501,9 +448,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") {
@@ -537,9 +482,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.
@@ -548,35 +491,22 @@
     "../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}}" ]
 }
 
 # +----------------------------------------------------------------------------+
 # | Node JS: Creates a symlink in the out directory to node_modules.           |
 # +----------------------------------------------------------------------------+
 
-action("check_node_exists") {
-  script = "../gn/standalone/check_buildtool_exists.py"
-  args = [
-    nodejs_bin,
-    "--touch",
-    rebase_path("$target_out_dir/node_exists", ""),
-  ]
-  inputs = []
-  outputs = [
-    "$target_out_dir/node_exists",
-  ]
+perfetto_check_build_deps("check_ui_deps") {
+  args = [ "--ui" ]
+  inputs = [ "package-lock.json" ]
 }
 
 # 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_ui_deps" ]
   script = "../gn/standalone/build_tool_wrapper.py"
   stamp_file = "$target_out_dir/.$target_name.stamp"
   args = [
@@ -587,15 +517,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
@@ -613,12 +539,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") {
@@ -629,21 +551,13 @@
 }
 
 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
@@ -659,14 +573,10 @@
       dist_files += [ rebase_path(dist_file, root_build_dir) ]
     }
   }
-  deps = [
-    ":dist",
-  ]
+  deps = [ ":dist" ]
   script = "../gn/standalone/write_ui_dist_file_map.py"
   inputs = []
-  outputs = [
-    out_file_path,
-  ]
+  outputs = [ out_file_path ]
   args = [
            "--out",
            rebase_path(out_file_path, root_build_dir),
diff --git a/ui/README.md b/ui/README.md
index 7fc13ac..ed0cd41 100644
--- a/ui/README.md
+++ b/ui/README.md
@@ -7,8 +7,8 @@
 ```bash
 $ git clone https://android.googlesource.com/platform/external/perfetto/
 $ cd perfetto
-$ tools/install-build-deps --no-android --ui
-$ tools/gn gen out/debug --args='is_debug=true is_clang=true'
+$ tools/install-build-deps --ui
+$ tools/gn gen out/debug --args='is_debug=true'
 $ tools/ninja -C out/debug ui
 ```
 
@@ -32,4 +32,4 @@
 $ ./ui/run-dev-server out/debug
 ```
 
-and navigate to `localhost:3000`.
+and navigate to `localhost:10000`.
diff --git a/ui/deploy b/ui/deploy
index 2923f1d..d10d188 100755
--- a/ui/deploy
+++ b/ui/deploy
@@ -81,6 +81,7 @@
 api_version: 1
 threadsafe: yes
 instance_class: B1
+default_expiration: "1m"
 manual_scaling:
   instances: 1
 handlers:
@@ -96,7 +97,6 @@
 - url: /assets/(.*)
   static_files: static/assets/\1
   upload: static/assets/(.*)
-  expiration: "1d"
 - url: /(.*)
   static_files: static/\1
   upload: static/(.*)
diff --git a/ui/index.html b/ui/index.html
index fae544d..a5dbde0 100644
--- a/ui/index.html
+++ b/ui/index.html
@@ -2,12 +2,12 @@
 <html lang="en-us">
 <head>
   <title>Perfetto UI</title>
+  <!-- See b/149573396 for CSP rationale. -->
+  <!-- TODO(b/121211019): remove script-src-elem rule once fixed. -->
+  <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src-elem 'self' https://*.google.com https://*.googleusercontent.com; object-src 'none'; connect-src 'self' http://127.0.0.1:9001 https://*.googleapis.com; navigate-to https://*.perfetto.dev;">
   <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport" />
-  <!-- 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=">
   <link href="perfetto.css" rel="stylesheet">
-  <link rel="icon" type="image/png" href="assets/logo.png">
+  <link rel="icon" type="image/png" href="assets/favicon.png">
 </head>
 <body>
   <main>
@@ -17,3 +17,4 @@
 </body>
 </html>
 <script src="frontend_bundle.js"></script>
+<script src="https://storage.cloud.google.com/perfetto-ui-internal/is_internal_user.js" async defer></script>
diff --git a/ui/node_modules/.gitignore b/ui/node_modules/.gitignore
index b15ad74..05849cd 100644
--- a/ui/node_modules/.gitignore
+++ b/ui/node_modules/.gitignore
@@ -1,2 +1,3 @@
 */
 custom_utils
+.last_install
diff --git a/ui/query.html b/ui/query.html
index 53bf614..bd45758 100644
--- a/ui/query.html
+++ b/ui/query.html
@@ -2,7 +2,7 @@
 <html lang="en-us">
 <head>
   <title>Perfetto - Query</title>
-  <link rel="icon" type="image/png" href="assets/logo.png">
+  <link rel="icon" type="image/png" href="assets/favicon.png">
   <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport"/>
   <style>
     * {
diff --git a/ui/src/assets/brand.png b/ui/src/assets/brand.png
new file mode 100644
index 0000000..dc6f8b6
--- /dev/null
+++ b/ui/src/assets/brand.png
Binary files differ
diff --git a/ui/src/assets/common.scss b/ui/src/assets/common.scss
index 4a272b8..6114874 100644
--- a/ui/src/assets/common.scss
+++ b/ui/src/assets/common.scss
@@ -17,6 +17,8 @@
     --monospace-font: 'Roboto Mono', monospace;
     --track-shell-width: 250px;
     --track-border-color: #00000025;
+    --anim-easing: cubic-bezier(0.4, 0.0, 0.2, 1)
+
 }
 
 @mixin transition($time:0.1s) {
@@ -137,7 +139,7 @@
     justify-content: center;
     align-items: center;
     flex-direction: row;
-    background-image: url('assets/logo.png');
+    background-image: url('assets/logo-3d.png');
     background-attachment: fixed;
     background-repeat: no-repeat;
     background-position: center;
@@ -260,6 +262,9 @@
         &:hover {
             background-color: hsl(214, 22%, 90%);
         }
+        &[clickable] {
+          cursor: pointer;
+        }
     }
 }
 
@@ -308,8 +313,8 @@
         padding-left: 10px;
         display: grid;
         cursor: grab;
-        grid-template-areas: "title pin";
-        grid-template-columns: 1fr auto auto;
+        grid-template-areas: "title buttons";
+        grid-template-columns: 1fr auto;
         align-items: center;
         width: var(--track-shell-width);
         background: #fff;
@@ -335,11 +340,17 @@
             color: hsl(213, 22%, 30%);
             @include track_shell_title();
         }
+        .track-buttons {
+          grid-area: buttons;
+          // This is necessary to center the buttons - but shouldn't be?
+          height: 18px;
+        }
         .track-button {
           @include transition();
-          color: #495767;
+          color: rgb(60, 86, 136);
           cursor: pointer;
-          width: 24px;
+          width: 22px;
+          font-size: 18px;
           opacity: 0;
         }
 
@@ -540,6 +551,9 @@
       border-right: 1px solid #c7d0db;
       background-color: var(--collapsed-background);
     }
+    .track-button {
+      color: rgb(60, 86, 136);
+    };
   }
   &[collapsed=false] {
     background-color: var(--expanded-background);
@@ -548,12 +562,15 @@
     .shell.flash {
       color: #121212;
     }
+    .track-button {
+      color: white;
+    }
   }
   .shell {
-    padding: 4px 10px;
+    padding: 4px 4px;
     display: grid;
-    grid-template-areas: "title fold-button";
-    grid-template-columns: 1fr 24px;
+    grid-template-areas: "fold-button title check";
+    grid-template-columns: 28px 1fr 20px;
     align-items: center;
     line-height: 1;
     width: var(--track-shell-width);
@@ -565,6 +582,9 @@
     .fold-button {
       grid-area: fold-button;
     }
+    .track-button {
+      font-size: 20px;
+    }
     &:hover {
       cursor: pointer;
       .fold-button {
diff --git a/ui/src/assets/details.scss b/ui/src/assets/details.scss
index 4d0fd2c..7dd5ff7 100644
--- a/ui/src/assets/details.scss
+++ b/ui/src/assets/details.scss
@@ -60,7 +60,6 @@
 
     i.material-icons {
       font-size: 24px;
-      vertical-align: middle;
       margin-right: 5px;
       margin-top: 1px;
       &:hover{
@@ -106,6 +105,15 @@
         font-weight: 500;
       }
     }
+    &.aggregation {
+      th {
+        cursor: pointer;
+        .material-icons {
+          margin-left: 2px;
+          font-size: 18px;
+        }
+      }
+    }
     h2 {
       font-size: 16px;
       font-weight: 400;
@@ -151,7 +159,6 @@
         .material-icons {
           font-size: 15px;
           margin-right: 3px;
-          vertical-align: middle;
         }
       }
       .title {
@@ -186,13 +193,17 @@
     .material-icons {
       @include transition(0.3s);
       font-size: 16px;
-      border-radius: 3px;
-      margin-left: 5px;
-      border: 1px solid transparent;
-      background-color: #e8e8e8;
+      margin-left: 5px; 
       &:hover {
         cursor: pointer;
-        border: #475566 solid 1px;
+      }
+      &.grey {
+        border-radius: 3px;
+        border: 1px solid transparent;
+        background-color: #e8e8e8;
+        &:hover {
+          border: #475566 solid 1px;
+        }
       }
     }
   }
diff --git a/ui/src/assets/favicon.png b/ui/src/assets/favicon.png
new file mode 100644
index 0000000..837b75b
--- /dev/null
+++ b/ui/src/assets/favicon.png
Binary files differ
diff --git a/ui/src/assets/logo-3d.png b/ui/src/assets/logo-3d.png
index 60626a9..b832ae6 100644
--- a/ui/src/assets/logo-3d.png
+++ b/ui/src/assets/logo-3d.png
Binary files differ
diff --git a/ui/src/assets/logo.png b/ui/src/assets/logo.png
deleted file mode 100644
index e003c1d..0000000
--- a/ui/src/assets/logo.png
+++ /dev/null
Binary files differ
diff --git a/ui/src/assets/rec_java_heap_dump.png b/ui/src/assets/rec_java_heap_dump.png
new file mode 100644
index 0000000..229ebe0
--- /dev/null
+++ b/ui/src/assets/rec_java_heap_dump.png
Binary files differ
diff --git a/ui/src/assets/heap_profiler.png b/ui/src/assets/rec_native_heap_profiler.png
similarity index 100%
rename from ui/src/assets/heap_profiler.png
rename to ui/src/assets/rec_native_heap_profiler.png
Binary files differ
diff --git a/ui/src/assets/record.scss b/ui/src/assets/record.scss
index b2170bb..4f98127 100644
--- a/ui/src/assets/record.scss
+++ b/ui/src/assets/record.scss
@@ -273,14 +273,14 @@
   background: #fff;
   transition: opacity 0.25s ease;
   opacity: 0;
-  visibility: hidden;
+  display: none;
 
   &:not(.active) {
     max-height: 0;
   }
 
   &.active {
-    visibility: visible;
+    display: grid;
     opacity: 1;
   }
 
@@ -730,7 +730,7 @@
   }
 
   .code-snippet {
-    display: block;
+    display: grid;
     position: relative;
     padding: 0;
     margin: var(--record-section-padding);
diff --git a/ui/src/assets/sidebar.scss b/ui/src/assets/sidebar.scss
index 7868f27..4a21ac4 100644
--- a/ui/src/assets/sidebar.scss
+++ b/ui/src/assets/sidebar.scss
@@ -14,13 +14,16 @@
 
 .sidebar {
     --sidebar-padding-bottom: 40px;
+    --sidebar-timing: 0.15s;
     grid-area: sidebar;
     z-index: 4;
     background-color: #262f3c;
-    overflow: visible;
+    overflow: hidden;
     width: var(--sidebar-width);
     display: flex;
     flex-direction: column;
+    transition: margin-left var(--anim-easing) var(--sidebar-timing),
+                visibility linear var(--sidebar-timing);
     >* {
         border-bottom: 1px solid #404854;
     }
@@ -36,30 +39,34 @@
         font-size: 24px;
         letter-spacing: 0.5px;
         overflow: visible;
-        &:before {
-            width: 32px;
-            height: 32px;
-            display: inline-block;
-            content: '';
-            vertical-align: middle;
-            background-image: url('assets/logo.png');
-            background-size: contain;
-            background-repeat: no-repeat;
-            margin-right: 15px;
+        .brand {
+          height: 40px;
+          margin-top: 4px;
         }
-        >.sidebar-button {
-          display: inline-block;
-          position: relative;
-          height: inherit;
-          background-color: #262f3c;
-          overflow: visible;
-          padding: 0 10px;
-          border-radius: 0 5px 5px 0;
-          border-bottom: inherit;
-          >button {
-            vertical-align: middle;
-          }
-        }
+    }
+    .sidebar-button {
+      position: absolute;
+      z-index: 5;
+      background-color: #262f3c;
+      height: var(--topbar-height);
+      left: calc(var(--sidebar-width) - 50px);
+      padding-top: 3px;
+      border-radius: 0 5px 5px 0;
+      border-bottom: inherit;
+      visibility: visible;  // So stays visible when the sidebar is hidden.
+      transition: left var(--anim-easing) var(--sidebar-timing);
+      width: 48px;
+      overflow: hidden;
+      >button {
+        vertical-align: middle;
+      }
+    }
+    &.hide-sidebar {
+      visibility: hidden;
+      margin-left: calc(var(--sidebar-width) * -1);
+      .sidebar-button {
+        left: 0;
+      }
     }
     .sidebar-scroll {
       overflow-y: auto;
@@ -153,7 +160,6 @@
                     }
                 }
                 .material-icons {
-                    vertical-align: middle;
                     margin-right: 10px;
                 }
                 &:hover {
@@ -214,17 +220,6 @@
     }
 }
 
-.hide-sidebar {
-  margin-left: calc(var(--sidebar-width) * -1);
-  .sidebar-button {
-    left: 96px;
-  }
-}
-
-.show-sidebar .sidebar-button {
-  left: 46px;
-}
-
 .keycap {
   background-color: #fafbfc;
   border: 1px solid #d1d5da;
diff --git a/ui/src/assets/topbar.scss b/ui/src/assets/topbar.scss
index 03a907f..fc5fa45 100644
--- a/ui/src/assets/topbar.scss
+++ b/ui/src/assets/topbar.scss
@@ -39,7 +39,6 @@
         line-height: 36px;
         &:before {
             @include material-icon('search');
-            vertical-align: middle;
             margin: 5px;
             color: #aaa;
             grid-area: icon;
@@ -106,11 +105,8 @@
           .current {
             padding-right: 10px;
           }
-          .material-icons {
-            vertical-align: middle;
-            &.left {
-              border-right: rgb(218, 217, 217) solid 1px;
-            }
+          .material-icons.left {
+            border-right: rgb(218, 217, 217) solid 1px;
           }
         }
     }
diff --git a/ui/src/assets/typefaces.scss b/ui/src/assets/typefaces.scss
index 55a2712..b5999b3 100644
--- a/ui/src/assets/typefaces.scss
+++ b/ui/src/assets/typefaces.scss
@@ -59,6 +59,7 @@
   line-height: 1;
   letter-spacing: normal;
   text-transform: none;
+  vertical-align: middle;
   display: inline-block;
   white-space: nowrap;
   word-wrap: normal;
diff --git a/ui/src/common/actions.ts b/ui/src/common/actions.ts
index c5f7f63..6cad41b 100644
--- a/ui/src/common/actions.ts
+++ b/ui/src/common/actions.ts
@@ -15,9 +15,14 @@
 import {Draft} from 'immer';
 
 import {assertExists} from '../base/logging';
-import {CallsiteInfo} from '../common/state';
+import {
+  Area,
+  CallsiteInfo,
+  HeapProfileFlamegraphViewingOption
+} from '../common/state';
 import {ConvertTrace, ConvertTraceToPprof} from '../controller/trace_converter';
 
+import {DEFAULT_VIEWING_OPTION} from './flamegraph_util';
 import {
   AdbRecordingTarget,
   createEmptyState,
@@ -183,6 +188,32 @@
     };
   },
 
+  updateAggregateSorting(
+      state: StateDraft, args: {id: string, column: string}) {
+    let prefs = state.aggregatePreferences[args.id];
+    if (!prefs) {
+      prefs = {id: args.id};
+      state.aggregatePreferences[args.id] = prefs;
+    }
+
+    if (!prefs.sorting || prefs.sorting.column !== args.column) {
+      // No sorting set for current column.
+      state.aggregatePreferences[args.id].sorting = {
+        column: args.column,
+        direction: 'DESC'
+      };
+    } else if (prefs.sorting.direction === 'DESC') {
+      // Toggle the direction if the column is currently sorted.
+      state.aggregatePreferences[args.id].sorting = {
+        column: args.column,
+        direction: 'ASC'
+      };
+    } else {
+      // If direction is currently 'ASC' toggle to no sorting.
+      state.aggregatePreferences[args.id].sorting = undefined;
+    }
+  },
+
   setVisibleTracks(state: StateDraft, args: {tracks: string[]}) {
     state.visibleTracks = args.tracks;
   },
@@ -337,11 +368,11 @@
       args: {timestamp: number, color: string, isMovie: boolean}): void {
     const id = `${state.nextId++}`;
     state.notes[id] = {
+      noteType: 'DEFAULT',
       id,
       timestamp: args.timestamp,
       color: args.color,
       text: '',
-      isMovie: args.isMovie
     };
     if (args.isMovie) {
       state.videoNoteIds.push(id);
@@ -349,6 +380,21 @@
     this.selectNote(state, {id});
   },
 
+  addAreaNote(
+      state: StateDraft, args: {timestamp: number, area: Area, color: string}):
+      void {
+        const id = `${state.nextId++}`;
+        state.notes[id] = {
+          noteType: 'AREA',
+          id,
+          timestamp: args.timestamp,
+          area: args.area,
+          color: args.color,
+          text: '',
+        };
+        this.selectNote(state, {id});
+      },
+
   toggleVideo(state: StateDraft, _: {}): void {
     state.videoEnabled = !state.videoEnabled;
     if (!state.videoEnabled) {
@@ -391,7 +437,7 @@
   },
 
   removeNote(state: StateDraft, args: {id: string}): void {
-    if (state.notes[args.id].isMovie) {
+    if (state.notes[args.id].noteType === 'MOVIE') {
       state.videoNoteIds = state.videoNoteIds.filter(id => {
         return id !== args.id;
       });
@@ -426,18 +472,28 @@
       },
 
   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,
+      viewingOption: DEFAULT_VIEWING_OPTION,
+      focusRegex: '',
     };
   },
 
@@ -448,11 +504,18 @@
   },
 
   changeViewHeapProfileFlamegraph(
-      state: StateDraft, args: {viewingOption: string}): void {
+      state: StateDraft,
+      args: {viewingOption: HeapProfileFlamegraphViewingOption}): void {
     if (state.currentHeapProfileFlamegraph === null) return;
     state.currentHeapProfileFlamegraph.viewingOption = args.viewingOption;
   },
 
+  changeFocusHeapProfileFlamegraph(
+      state: StateDraft, args: {focusRegex: string}): void {
+    if (state.currentHeapProfileFlamegraph === null) return;
+    state.currentHeapProfileFlamegraph.focusRegex = args.focusRegex;
+  },
+
   selectChromeSlice(state: StateDraft, args: {id: number, trackId: string}):
       void {
         state.currentSelection = {
diff --git a/ui/src/common/aggregation_data.ts b/ui/src/common/aggregation_data.ts
index 3823150..2558e17 100644
--- a/ui/src/common/aggregation_data.ts
+++ b/ui/src/common/aggregation_data.ts
@@ -12,12 +12,40 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-export interface AggregateCpuData {
-  strings: string[];
-  procNameId: Uint16Array;
-  pid: Uint32Array;
-  threadNameId: Uint16Array;
-  tid: Uint32Array;
-  totalDur: Float64Array;
-  occurrences: Uint16Array;
+export type Column = (StringColumn|TimestampColumn|NumberColumn|StateColumn)&
+    {title: string, columnId: string};
+
+export interface StringColumn {
+  kind: 'STRING';
+  data: Uint16Array;
 }
+
+export interface TimestampColumn {
+  kind: 'TIMESTAMP_NS';
+  data: Float64Array;
+}
+
+export interface NumberColumn {
+  kind: 'NUMBER';
+  data: Uint16Array;
+}
+
+export interface StateColumn {
+  kind: 'STATE';
+  data: Uint16Array;
+}
+
+type TypedArrayConstructor = Uint16ArrayConstructor|Float64ArrayConstructor;
+export interface ColumnDef {
+  title: string;
+  kind: string;
+  columnConstructor: TypedArrayConstructor;
+  columnId: string;
+}
+
+export interface AggregateData {
+  tabName: string;
+  columns: Column[];
+  // For string interning.
+  strings: string[];
+}
\ No newline at end of file
diff --git a/ui/src/common/engine.ts b/ui/src/common/engine.ts
index b7734e3..4c5a1cd 100644
--- a/ui/src/common/engine.ts
+++ b/ui/src/common/engine.ts
@@ -12,7 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {RawQueryArgs, RawQueryResult} from './protos';
+import {
+  ComputeMetricArgs,
+  ComputeMetricResult,
+  RawQueryArgs,
+  RawQueryResult
+} from './protos';
 import {TimeSpan} from './time';
 
 export interface LoadingTracker {
@@ -64,11 +69,17 @@
    */
   abstract rawQuery(rawQueryArgs: Uint8Array): Promise<Uint8Array>;
 
+  /*
+   * Performs computation of metrics and returns a proto-encoded TraceMetrics
+   * object.
+   */
+  abstract rawComputeMetric(computeMetricArgs: Uint8Array): Promise<Uint8Array>;
+
   /**
    * Shorthand for sending a SQL query to the engine.
    * Deals with {,un}marshalling of request/response args.
    */
-  async query(sqlQuery: string): Promise<RawQueryResult> {
+  async query(sqlQuery: string, userQuery = false): Promise<RawQueryResult> {
     this.loadingTracker.beginLoading();
     try {
       const args = new RawQueryArgs();
@@ -76,16 +87,43 @@
       args.timeQueuedNs = Math.floor(performance.now() * 1e6);
       const argsEncoded = RawQueryArgs.encode(args).finish();
       const respEncoded = await this.rawQuery(argsEncoded);
-      return RawQueryResult.decode(respEncoded);
+      const result = RawQueryResult.decode(respEncoded);
+      if (!result.error || userQuery) return result;
+      // Query failed, throw an error since it was not a user query
+      console.error(`Query error "${sqlQuery}": ${result.error}`);
+      throw new Error(`Query error "${sqlQuery}": ${result.error}`);
     } finally {
       this.loadingTracker.endLoading();
     }
   }
 
+  /**
+   * Shorthand for sending a compute metrics request to the engine.
+   * Deals with {,un}marshalling of request/response args.
+   */
+  async computeMetric(metrics: string[]): Promise<ComputeMetricResult> {
+    const args = new ComputeMetricArgs();
+    args.metricNames = metrics;
+    const argsEncoded = ComputeMetricArgs.encode(args).finish();
+    const respEncoded = await this.rawComputeMetric(argsEncoded);
+    return ComputeMetricResult.decode(respEncoded);
+  }
+
   async queryOneRow(query: string): Promise<number[]> {
     const result = await this.query(query);
     const res: number[] = [];
-    result.columns.map(c => res.push(+c.longValues![0]));
+    if (result.numRecords === 0) return res;
+    for (const col of result.columns) {
+      if (col.longValues!.length === 0) {
+        console.error(
+            `queryOneRow should only be used for queries that return long values
+             : ${query}`);
+        throw new Error(
+            `queryOneRow should only be used for queries that return long values
+             : ${query}`);
+      }
+      res.push(+col.longValues![0]);
+    }
     return res;
   }
 
diff --git a/ui/src/common/flamegraph_unittest.ts b/ui/src/common/flamegraph_unittest.ts
index fc19250..dfb03a4 100644
--- a/ui/src/common/flamegraph_unittest.ts
+++ b/ui/src/common/flamegraph_unittest.ts
@@ -24,7 +24,8 @@
       depth: 0,
       totalSize: 10,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 2,
@@ -33,7 +34,8 @@
       depth: 0,
       totalSize: 8,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 3,
@@ -42,7 +44,8 @@
       depth: 1,
       totalSize: 4,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 4,
@@ -51,7 +54,8 @@
       depth: 1,
       totalSize: 4,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
   ];
 
@@ -70,7 +74,8 @@
       depth: 0,
       totalSize: 10,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 2,
@@ -79,7 +84,8 @@
       depth: 0,
       totalSize: 8,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 3,
@@ -88,7 +94,8 @@
       depth: 1,
       totalSize: 6,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 4,
@@ -97,7 +104,8 @@
       depth: 1,
       totalSize: 4,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 5,
@@ -106,7 +114,8 @@
       depth: 1,
       totalSize: 8,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
   ];
 
@@ -125,7 +134,8 @@
       depth: 0,
       totalSize: 10,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 2,
@@ -134,7 +144,8 @@
       depth: 1,
       totalSize: 5,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 3,
@@ -143,7 +154,8 @@
       depth: 1,
       totalSize: 5,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
   ];
 
@@ -157,16 +169,18 @@
       depth: 0,
       totalSize: 10,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 2,
       parentId: 1,
-      name: 'A2',
+      name: '[merged]',
       depth: 1,
       totalSize: 10,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: true
     },
   ]);
 });
@@ -180,7 +194,8 @@
       depth: 0,
       totalSize: 10,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 2,
@@ -189,7 +204,8 @@
       depth: 1,
       totalSize: 5,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 3,
@@ -198,7 +214,8 @@
       depth: 1,
       totalSize: 3,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 4,
@@ -207,7 +224,8 @@
       depth: 1,
       totalSize: 1,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 5,
@@ -216,7 +234,8 @@
       depth: 1,
       totalSize: 1,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 6,
@@ -225,7 +244,8 @@
       depth: 2,
       totalSize: 1,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 7,
@@ -234,7 +254,8 @@
       depth: 2,
       totalSize: 1,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 8,
@@ -243,7 +264,8 @@
       depth: 2,
       totalSize: 1,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
   ];
 
@@ -255,7 +277,8 @@
       depth: 0,
       totalSize: 10,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 2,
@@ -264,25 +287,28 @@
       depth: 1,
       totalSize: 5,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 3,
       parentId: 1,
-      name: 'A3',
+      name: '[merged]',
       depth: 1,
       totalSize: 5,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: true
     },
     {
       id: 6,
       parentId: 3,
-      name: 'A36',
+      name: '[merged]',
       depth: 2,
       totalSize: 3,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: true
     },
   ];
 
@@ -303,7 +329,8 @@
       depth: 0,
       totalSize: 5,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 2,
@@ -312,7 +339,8 @@
       depth: 0,
       totalSize: 5,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 3,
@@ -321,7 +349,8 @@
       depth: 1,
       totalSize: 3,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 4,
@@ -330,7 +359,8 @@
       depth: 1,
       totalSize: 1,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 5,
@@ -339,7 +369,8 @@
       depth: 1,
       totalSize: 1,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 6,
@@ -348,7 +379,8 @@
       depth: 1,
       totalSize: 5,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 7,
@@ -357,7 +389,8 @@
       depth: 2,
       totalSize: 1,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 8,
@@ -366,7 +399,8 @@
       depth: 2,
       totalSize: 1,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
   ];
 
@@ -378,7 +412,8 @@
       depth: 0,
       totalSize: 5,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 2,
@@ -387,16 +422,18 @@
       depth: 0,
       totalSize: 5,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 3,
       parentId: 1,
-      name: 'A3',
+      name: '[merged]',
       depth: 1,
       totalSize: 5,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: true
     },
     {
       id: 6,
@@ -405,7 +442,8 @@
       depth: 1,
       totalSize: 5,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 7,
@@ -414,7 +452,8 @@
       depth: 2,
       totalSize: 1,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 8,
@@ -423,7 +462,8 @@
       depth: 2,
       totalSize: 1,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
   ];
 
@@ -445,7 +485,8 @@
       depth: 0,
       totalSize: 20,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 2,
@@ -454,7 +495,8 @@
       depth: 1,
       totalSize: 8,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 3,
@@ -463,7 +505,8 @@
       depth: 1,
       totalSize: 1,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 4,
@@ -472,7 +515,8 @@
       depth: 1,
       totalSize: 8,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 5,
@@ -481,7 +525,8 @@
       depth: 1,
       totalSize: 3,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
   ];
 
@@ -493,7 +538,8 @@
       depth: 0,
       totalSize: 20,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 2,
@@ -502,16 +548,18 @@
       depth: 1,
       totalSize: 8,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 3,
       parentId: 1,
-      name: 'A3',
+      name: '[merged]',
       depth: 1,
       totalSize: 4,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: true
     },
     {
       id: 4,
@@ -520,7 +568,8 @@
       depth: 1,
       totalSize: 8,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
   ];
 
@@ -542,7 +591,8 @@
       depth: 0,
       totalSize: 10,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 2,
@@ -551,7 +601,8 @@
       depth: 1,
       totalSize: 2,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 3,
@@ -560,7 +611,8 @@
       depth: 1,
       totalSize: 2,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
   ];
 
@@ -578,7 +630,8 @@
       depth: 0,
       totalSize: 10,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 2,
@@ -587,7 +640,8 @@
       depth: 0,
       totalSize: 2,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
   ];
 
@@ -597,11 +651,12 @@
     {
       id: 1,
       parentId: -1,
-      name: 'A',
+      name: '[merged]',
       depth: 0,
       totalSize: 12,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: true
     },
   ]);
 });
@@ -615,7 +670,8 @@
       depth: 0,
       totalSize: 60,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 2,
@@ -624,7 +680,8 @@
       depth: 0,
       totalSize: 40,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 3,
@@ -633,7 +690,8 @@
       depth: 1,
       totalSize: 25,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 4,
@@ -642,7 +700,8 @@
       depth: 1,
       totalSize: 15,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 5,
@@ -651,7 +710,8 @@
       depth: 1,
       totalSize: 10,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 6,
@@ -660,7 +720,8 @@
       depth: 1,
       totalSize: 10,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 7,
@@ -669,7 +730,8 @@
       depth: 1,
       totalSize: 30,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 8,
@@ -678,7 +740,8 @@
       depth: 1,
       totalSize: 10,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 9,
@@ -687,7 +750,8 @@
       depth: 2,
       totalSize: 20,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 10,
@@ -696,7 +760,8 @@
       depth: 2,
       totalSize: 10,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 11,
@@ -705,7 +770,8 @@
       depth: 2,
       totalSize: 3,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 12,
@@ -714,7 +780,8 @@
       depth: 2,
       totalSize: 2,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 13,
@@ -723,7 +790,8 @@
       depth: 2,
       totalSize: 5,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 14,
@@ -732,7 +800,8 @@
       depth: 2,
       totalSize: 5,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 15,
@@ -741,7 +810,8 @@
       depth: 2,
       totalSize: 10,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 16,
@@ -750,7 +820,8 @@
       depth: 2,
       totalSize: 5,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 17,
@@ -759,7 +830,8 @@
       depth: 2,
       totalSize: 5,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 18,
@@ -768,7 +840,8 @@
       depth: 2,
       totalSize: 5,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 19,
@@ -777,7 +850,8 @@
       depth: 3,
       totalSize: 10,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 20,
@@ -786,7 +860,8 @@
       depth: 3,
       totalSize: 2,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
   ];
 
@@ -798,7 +873,8 @@
       depth: 0,
       totalSize: 60,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 2,
@@ -807,7 +883,8 @@
       depth: 0,
       totalSize: 40,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 3,
@@ -816,16 +893,18 @@
       depth: 1,
       totalSize: 25,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 4,
       parentId: 1,
-      name: 'A4',
+      name: '[merged]',
       depth: 1,
       totalSize: 35,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: true
     },
     {
       id: 7,
@@ -834,7 +913,8 @@
       depth: 1,
       totalSize: 30,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 8,
@@ -843,7 +923,8 @@
       depth: 1,
       totalSize: 10,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 9,
@@ -852,25 +933,28 @@
       depth: 2,
       totalSize: 20,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 10,
       parentId: 4,
-      name: 'A410',
+      name: '[merged]',
       depth: 2,
       totalSize: 25,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: true
     },
     {
       id: 15,
       parentId: 7,
-      name: 'A715',
+      name: '[merged]',
       depth: 2,
       totalSize: 25,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: true
     },
     {
       id: 19,
@@ -879,7 +963,8 @@
       depth: 3,
       totalSize: 10,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
     {
       id: 20,
@@ -888,7 +973,8 @@
       depth: 3,
       totalSize: 2,
       selfSize: 0,
-      mapping: 'x'
+      mapping: 'x',
+      merged: false
     },
   ];
 
diff --git a/ui/src/common/flamegraph_util.ts b/ui/src/common/flamegraph_util.ts
index f4f4d16..a94eddd 100644
--- a/ui/src/common/flamegraph_util.ts
+++ b/ui/src/common/flamegraph_util.ts
@@ -14,10 +14,10 @@
 
 import {CallsiteInfo} from '../common/state';
 
-export const SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY = 'space';
-export const ALLOC_SPACE_MEMORY_ALLOCATED_KEY = 'alloc_space';
-export const OBJECTS_ALLOCATED_NOT_FREED_KEY = 'objects';
-export const OBJECTS_ALLOCATED_KEY = 'alloc_objects';
+export const SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY = 'SPACE';
+export const ALLOC_SPACE_MEMORY_ALLOCATED_KEY = 'ALLOC_SPACE';
+export const OBJECTS_ALLOCATED_NOT_FREED_KEY = 'OBJECTS';
+export const OBJECTS_ALLOCATED_KEY = 'ALLOC_OBJECTS';
 
 export const DEFAULT_VIEWING_OPTION = SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY;
 
@@ -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,15 @@
             nextCallsite.totalSize <= minSizeDisplayed) {
           copiedCallsite.totalSize += nextCallsite.totalSize;
           mergedCallsites.set(nextCallsite.id, copiedCallsite.id);
+          mergedAny = true;
         }
         j++;
         nextCallsite = data[j];
       }
+      if (mergedAny) {
+        copiedCallsite.name = '[merged]';
+        copiedCallsite.merged = true;
+      }
     }
     mergedData.push(copiedCallsite);
   }
@@ -95,7 +101,8 @@
     name: callsite.name,
     totalSize: callsite.totalSize,
     mapping: callsite.mapping,
-    selfSize: callsite.selfSize
+    selfSize: callsite.selfSize,
+    merged: callsite.merged,
   };
 }
 
@@ -112,4 +119,4 @@
     i++;
   }
   return totalSize;
-}
\ No newline at end of file
+}
diff --git a/ui/src/common/http_rpc_engine.ts b/ui/src/common/http_rpc_engine.ts
index e0da547..723eddd 100644
--- a/ui/src/common/http_rpc_engine.ts
+++ b/ui/src/common/http_rpc_engine.ts
@@ -60,6 +60,10 @@
     return this.enqueueRequest('raw_query', rawQueryArgs);
   }
 
+  rawComputeMetric(rawComputeMetricArgs: Uint8Array): Promise<Uint8Array> {
+    return this.enqueueRequest('compute_metric', rawComputeMetricArgs);
+  }
+
   enqueueRequest(methodName: string, data?: Uint8Array): Promise<Uint8Array> {
     const resp = defer<Uint8Array>();
     this.requestQueue.push({methodName, reqData: data, resp});
diff --git a/ui/src/common/protos.ts b/ui/src/common/protos.ts
index 49533d8..52160fd 100644
--- a/ui/src/common/protos.ts
+++ b/ui/src/common/protos.ts
@@ -25,11 +25,14 @@
 import BufferConfig = protos.perfetto.protos.TraceConfig.BufferConfig;
 import ChromeConfig = protos.perfetto.protos.ChromeConfig;
 import ConsumerPort = protos.perfetto.protos.ConsumerPort;
-import ContinuousDumpConfig =
+import NativeContinuousDumpConfig =
     protos.perfetto.protos.HeapprofdConfig.ContinuousDumpConfig;
+import JavaContinuousDumpConfig =
+    protos.perfetto.protos.JavaHprofConfig.ContinuousDumpConfig;
 import DataSourceConfig = protos.perfetto.protos.DataSourceConfig;
 import FtraceConfig = protos.perfetto.protos.FtraceConfig;
 import HeapprofdConfig = protos.perfetto.protos.HeapprofdConfig;
+import JavaHprofConfig = protos.perfetto.protos.JavaHprofConfig;
 import IAndroidPowerConfig = protos.perfetto.protos.IAndroidPowerConfig;
 import IBufferConfig = protos.perfetto.protos.TraceConfig.IBufferConfig;
 import IProcessStatsConfig = protos.perfetto.protos.IProcessStatsConfig;
@@ -43,10 +46,12 @@
 import VmstatCounters = protos.perfetto.protos.VmstatCounters;
 
 // Trace Processor protos.
-import IRawQueryArgs = protos.perfetto.trace_processor.protos.IRawQueryArgs;
-import RawQueryArgs = protos.perfetto.trace_processor.protos.RawQueryArgs;
-import RawQueryResult = protos.perfetto.trace_processor.protos.RawQueryResult;
-import StatusResult = protos.perfetto.trace_processor.protos.StatusResult;
+import IRawQueryArgs = protos.perfetto.protos.IRawQueryArgs;
+import RawQueryArgs = protos.perfetto.protos.RawQueryArgs;
+import RawQueryResult = protos.perfetto.protos.RawQueryResult;
+import StatusResult = protos.perfetto.protos.StatusResult;
+import ComputeMetricArgs = protos.perfetto.protos.ComputeMetricArgs;
+import ComputeMetricResult = protos.perfetto.protos.ComputeMetricResult;
 
 // TODO(hjd): Maybe these should go in their own file.
 export interface Row { [key: string]: number|string; }
@@ -207,7 +212,8 @@
   BufferConfig,
   ChromeConfig,
   ConsumerPort,
-  ContinuousDumpConfig,
+  ComputeMetricArgs,
+  ComputeMetricResult,
   DataSourceConfig,
   FtraceConfig,
   HeapprofdConfig,
@@ -217,12 +223,15 @@
   IRawQueryArgs,
   ISysStatsConfig,
   ITraceConfig,
+  JavaContinuousDumpConfig,
+  JavaHprofConfig,
   MeminfoCounters,
+  NativeContinuousDumpConfig,
   ProcessStatsConfig,
   RawQueryArgs,
   RawQueryResult,
-  StatusResult,
   StatCounters,
+  StatusResult,
   SysStatsConfig,
   TraceConfig,
   VmstatCounters,
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index 2b6d2b2..49e5a98 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -48,6 +48,9 @@
 
 export type NewEngineMode = 'USE_HTTP_RPC_IF_AVAILABLE'|'FORCE_BUILTIN_WASM';
 
+export type HeapProfileFlamegraphViewingOption =
+    'SPACE'|'ALLOC_SPACE'|'OBJECTS'|'ALLOC_OBJECTS';
+
 export interface CallsiteInfo {
   id: number;
   parentId: number;
@@ -56,6 +59,7 @@
   totalSize: number;
   selfSize: number;
   mapping: string;
+  merged: boolean;
 }
 
 export interface TraceFileSource {
@@ -134,11 +138,20 @@
 }
 
 export interface Note {
+  noteType: 'DEFAULT'|'MOVIE';
   id: string;
   timestamp: number;
   color: string;
   text: string;
-  isMovie: boolean;
+}
+
+export interface AreaNote {
+  noteType: 'AREA';
+  id: string;
+  timestamp: number;
+  area: Area;
+  color: string;
+  text: string;
 }
 
 export interface NoteSelection {
@@ -163,6 +176,7 @@
   id: number;
   upid: number;
   ts: number;
+  type: string;
 }
 
 export interface HeapProfileFlamegraph {
@@ -170,8 +184,10 @@
   id: number;
   upid: number;
   ts: number;
+  type: string;
+  viewingOption: HeapProfileFlamegraphViewingOption;
+  focusRegex: string;
   expandedCallsite?: CallsiteInfo;
-  viewingOption?: string;
 }
 
 export interface ChromeSliceSelection {
@@ -206,6 +222,16 @@
   serial: string;
 }
 
+export interface Sorting {
+  column: string;
+  direction: 'DESC'|'ASC';
+}
+
+export interface AggregationState {
+  id: string;
+  sorting?: Sorting;
+}
+
 export interface State {
   // tslint:disable-next-line:no-any
   [key: string]: any;
@@ -226,12 +252,13 @@
   traceTime: TraceTime;
   trackGroups: ObjectById<TrackGroupState>;
   tracks: ObjectById<TrackState>;
+  aggregatePreferences: ObjectById<AggregationState>;
   visibleTracks: string[];
   scrollingTracks: string[];
   pinnedTracks: string[];
   queries: ObjectById<QueryConfig>;
   permalink: PermalinkConfig;
-  notes: ObjectById<Note>;
+  notes: ObjectById<Note|AreaNote>;
   status: Status;
   currentSelection: Selection|null;
   currentHeapProfileFlamegraph: HeapProfileFlamegraph|null;
@@ -348,6 +375,11 @@
   hpContinuousDumpsInterval: number;
   hpSharedMemoryBuffer: number;
 
+  javaHeapDump: boolean;
+  jpProcesses: string;
+  jpContinuousDumpsPhase: number;
+  jpContinuousDumpsInterval: number;
+
   procStats: boolean;
   procStatsPeriodMs: number;
 
@@ -406,6 +438,11 @@
     hpContinuousDumpsInterval: 0,
     hpSharedMemoryBuffer: 8 * 1048576,
 
+    javaHeapDump: false,
+    jpProcesses: '',
+    jpContinuousDumpsPhase: 0,
+    jpContinuousDumpsInterval: 0,
+
     memLmk: false,
     procStats: false,
     procStatsPeriodMs: 1000,
@@ -640,6 +677,7 @@
     engines: {},
     traceTime: {...defaultTraceTime},
     tracks: {},
+    aggregatePreferences: {},
     trackGroups: {},
     visibleTracks: [],
     pinnedTracks: [],
@@ -665,7 +703,7 @@
       },
       selectedArea: {
         lastUpdate: 0,
-      },
+      }
     },
 
     logsPagination: {
diff --git a/ui/src/common/thread_state.ts b/ui/src/common/thread_state.ts
index 1215f4f..97abe2e 100644
--- a/ui/src/common/thread_state.ts
+++ b/ui/src/common/thread_state.ts
@@ -14,8 +14,8 @@
 
 const states: {[key: string]: string} = {
   'R': 'Runnable',
-  'S': 'Interruptible Sleep',
-  'D': 'Uninterruptible (Disk) Sleep',
+  'S': 'Sleeping',
+  'D': 'Uninterruptible Sleep',
   'T': 'Stopped',
   't': 'Traced',
   'X': 'Exit (Dead)',
@@ -30,7 +30,7 @@
 
 export function translateState(state: string|undefined) {
   if (state === undefined) return '';
-  if (state === 'Running' || state === 'Runnable' || state === 'Busy') {
+  if (state === 'Running' || state === 'Various states') {
     return state;
   }
   let result = states[state[0]];
diff --git a/ui/src/common/wasm_engine_proxy.ts b/ui/src/common/wasm_engine_proxy.ts
index b09c662..c5e1e94 100644
--- a/ui/src/common/wasm_engine_proxy.ts
+++ b/ui/src/common/wasm_engine_proxy.ts
@@ -109,6 +109,11 @@
     return this.queueRequest('trace_processor_raw_query', rawQueryArgs);
   }
 
+  rawComputeMetric(rawComputeMetric: Uint8Array): Promise<Uint8Array> {
+    return this.queueRequest(
+        'trace_processor_compute_metric', rawComputeMetric);
+  }
+
   // Enqueues a request to the worker queue via postMessage(). The returned
   // promised will be resolved once the worker replies to the postMessage()
   // with the paylad of the response, a proto-encoded object which wraps the
diff --git a/ui/src/controller/aggregation/aggregation_controller.ts b/ui/src/controller/aggregation/aggregation_controller.ts
new file mode 100644
index 0000000..8ce400a
--- /dev/null
+++ b/ui/src/controller/aggregation/aggregation_controller.ts
@@ -0,0 +1,144 @@
+// 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 {AggregateData, Column, ColumnDef} from '../../common/aggregation_data';
+import {Engine} from '../../common/engine';
+import {Sorting, TimestampedAreaSelection} from '../../common/state';
+import {Controller} from '../controller';
+import {globals} from '../globals';
+
+export interface AggregationControllerArgs {
+  engine: Engine;
+  kind: string;
+}
+
+export abstract class AggregationController extends Controller<'main'> {
+  readonly kind: string;
+  private previousArea: TimestampedAreaSelection = {lastUpdate: 0};
+  private previousSorting?: Sorting;
+  private requestingData = false;
+  private queuedRequest = false;
+
+  abstract async createAggregateView(
+      engine: Engine, area: TimestampedAreaSelection): Promise<boolean>;
+
+  abstract getTabName(): string;
+  abstract getDefaultSorting(): Sorting;
+  abstract getColumnDefinitions(): ColumnDef[];
+
+  constructor(private args: AggregationControllerArgs) {
+    super('main');
+    this.kind = this.args.kind;
+  }
+
+  run() {
+    const selectedArea = globals.state.frontendLocalState.selectedArea;
+    const aggregatePreferences =
+        globals.state.aggregatePreferences[this.args.kind];
+
+    const areaChanged = this.previousArea.lastUpdate < selectedArea.lastUpdate;
+    const sortingChanged = aggregatePreferences &&
+        this.previousSorting !== aggregatePreferences.sorting;
+    if (!areaChanged && !sortingChanged) return;
+
+    if (this.requestingData) {
+      this.queuedRequest = true;
+    } else {
+      this.requestingData = true;
+      if (sortingChanged) this.previousSorting = aggregatePreferences.sorting;
+      if (areaChanged) this.previousArea = selectedArea;
+      this.getAggregateData(areaChanged)
+          .then(
+              data => globals.publish(
+                  'AggregateData', {data, kind: this.args.kind}))
+          .catch(reason => {
+            console.error(reason);
+          })
+          .finally(() => {
+            this.requestingData = false;
+            if (this.queuedRequest) {
+              this.queuedRequest = false;
+              this.run();
+            }
+          });
+    }
+  }
+
+  async getAggregateData(areaChanged: boolean): Promise<AggregateData> {
+    const selectedArea = globals.state.frontendLocalState.selectedArea;
+    if (areaChanged) {
+      const viewExists =
+          await this.createAggregateView(this.args.engine, selectedArea);
+      if (!viewExists) {
+        return {tabName: this.getTabName(), columns: [], strings: []};
+      }
+    }
+
+    const defs = this.getColumnDefinitions();
+    const colIds = defs.map(col => col.columnId);
+    const pref = globals.state.aggregatePreferences[this.kind];
+    let sorting = `${this.getDefaultSorting().column} ${
+        this.getDefaultSorting().direction}`;
+    if (pref && pref.sorting) {
+      sorting = `${pref.sorting.column} ${pref.sorting.direction}`;
+    }
+    const query = `select ${colIds} from ${this.kind} order by ${sorting}`;
+    const result = await this.args.engine.query(query);
+
+    const numRows = +result.numRecords;
+    const columns = defs.map(def => this.columnFromColumnDef(def, numRows));
+
+    const data:
+        AggregateData = {tabName: this.getTabName(), columns, strings: []};
+
+    const stringIndexes = new Map<string, number>();
+    function internString(str: string) {
+      let idx = stringIndexes.get(str);
+      if (idx !== undefined) return idx;
+      idx = data.strings.length;
+      data.strings.push(str);
+      stringIndexes.set(str, idx);
+      return idx;
+    }
+
+    for (let row = 0; row < numRows; row++) {
+      const cols = result.columns;
+      for (let col = 0; col < result.columns.length; col++) {
+        if (cols[col].stringValues && cols[col].stringValues!.length > 0) {
+          data.columns[col].data[row] =
+              internString(cols[col].stringValues![row]);
+        } else if (cols[col].longValues && cols[col].longValues!.length > 0) {
+          data.columns[col].data[row] = cols[col].longValues![row] as number;
+        } else if (
+            cols[col].doubleValues && cols[col].doubleValues!.length > 0) {
+          data.columns[col].data[row] = cols[col].doubleValues![row];
+        }
+      }
+    }
+    return data;
+  }
+
+  columnFromColumnDef(def: ColumnDef, numRows: number): Column {
+    // TODO(taylori): The Column type should be based on the
+    // ColumnDef type or vice versa to avoid this cast.
+    return {
+      title: def.title,
+      kind: def.kind,
+      data: new def.columnConstructor(numRows),
+      columnId: def.columnId,
+    } as Column;
+  }
+}
diff --git a/ui/src/controller/aggregation/cpu_aggregation_controller.ts b/ui/src/controller/aggregation/cpu_aggregation_controller.ts
new file mode 100644
index 0000000..fcbc57a
--- /dev/null
+++ b/ui/src/controller/aggregation/cpu_aggregation_controller.ts
@@ -0,0 +1,113 @@
+// 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 {ColumnDef} from '../../common/aggregation_data';
+import {Engine} from '../../common/engine';
+import {Sorting, TimestampedAreaSelection} from '../../common/state';
+import {toNs} from '../../common/time';
+import {Config, CPU_SLICE_TRACK_KIND} from '../../tracks/cpu_slices/common';
+import {globals} from '../globals';
+
+import {AggregationController} from './aggregation_controller';
+
+
+export class CpuAggregationController extends AggregationController {
+  async createAggregateView(
+      engine: Engine, selectedArea: TimestampedAreaSelection) {
+    await engine.query(`drop view if exists ${this.kind};`);
+    const area = selectedArea.area;
+    if (area === undefined) return false;
+
+    const selectedCpus = [];
+    for (const trackId of area.tracks) {
+      const track = globals.state.tracks[trackId];
+      // Track will be undefined for track groups.
+      if (track !== undefined && track.kind === CPU_SLICE_TRACK_KIND) {
+        selectedCpus.push((track.config as Config).cpu);
+      }
+    }
+    if (selectedCpus.length === 0) return false;
+
+    const query = `create view ${this.kind} as
+        SELECT process.name as process_name, pid, thread.name as thread_name,
+        tid, sum(dur) AS total_dur,
+        sum(dur)/count(1) as avg_dur,
+        count(1) as occurrences
+        FROM process
+        JOIN thread USING(upid)
+        JOIN thread_state USING(utid)
+        WHERE cpu IN (${selectedCpus}) AND
+        state = "Running" AND
+        thread_state.ts + thread_state.dur > ${toNs(area.startSec)} AND
+        thread_state.ts < ${toNs(area.endSec)} group by utid`;
+
+    await engine.query(query);
+    return true;
+  }
+
+  getTabName() {
+    return 'CPU Slices';
+  }
+
+  getDefaultSorting(): Sorting {
+    return {column: 'total_dur', direction: 'DESC'};
+  }
+
+  getColumnDefinitions(): ColumnDef[] {
+    return [
+      {
+        title: 'Process',
+        kind: 'STRING',
+        columnConstructor: Uint16Array,
+        columnId: 'process_name',
+      },
+      {
+        title: 'PID',
+        kind: 'NUMBER',
+        columnConstructor: Uint16Array,
+        columnId: 'pid'
+      },
+      {
+        title: 'Thread',
+        kind: 'STRING',
+        columnConstructor: Uint16Array,
+        columnId: 'thread_name'
+      },
+      {
+        title: 'TID',
+        kind: 'NUMBER',
+        columnConstructor: Uint16Array,
+        columnId: 'tid'
+      },
+      {
+        title: 'Wall duration (ms)',
+        kind: 'TIMESTAMP_NS',
+        columnConstructor: Float64Array,
+        columnId: 'total_dur'
+      },
+      {
+        title: 'Avg Wall duration (ms)',
+        kind: 'TIMESTAMP_NS',
+        columnConstructor: Float64Array,
+        columnId: 'avg_dur'
+      },
+      {
+        title: 'Occurrences',
+        kind: 'NUMBER',
+        columnConstructor: Uint16Array,
+        columnId: 'occurrences'
+      }
+    ];
+  }
+}
diff --git a/ui/src/controller/aggregation/thread_aggregation_controller.ts b/ui/src/controller/aggregation/thread_aggregation_controller.ts
new file mode 100644
index 0000000..b781275
--- /dev/null
+++ b/ui/src/controller/aggregation/thread_aggregation_controller.ts
@@ -0,0 +1,124 @@
+// 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 {ColumnDef} from '../../common/aggregation_data';
+import {Engine} from '../../common/engine';
+import {Sorting, TimestampedAreaSelection} from '../../common/state';
+import {toNs} from '../../common/time';
+import {
+  Config,
+  THREAD_STATE_TRACK_KIND
+} from '../../tracks/thread_state/common';
+import {globals} from '../globals';
+
+import {AggregationController} from './aggregation_controller';
+
+export class ThreadAggregationController extends AggregationController {
+  async createAggregateView(
+      engine: Engine, selectedArea: TimestampedAreaSelection) {
+    await engine.query(`drop view if exists ${this.kind};`);
+    const area = selectedArea.area;
+    if (area === undefined) return false;
+
+    // TODO(taylori): Thread state tracks should have a real track id in the
+    // trace processor.
+    const utids = [];
+    for (const trackId of area.tracks) {
+      const track = globals.state.tracks[trackId];
+      // Track will be undefined for track groups.
+      if (track !== undefined && track.kind === THREAD_STATE_TRACK_KIND) {
+        utids.push((track.config as Config).utid);
+      }
+    }
+    if (utids.length === 0) return false;
+
+    const query = `create view ${this.kind} as
+      SELECT process.name as process_name, pid, thread.name as thread_name, tid,
+      state,
+      sum(dur) AS total_dur,
+      sum(dur)/count(1) as avg_dur,
+      count(1) as occurrences
+      FROM process
+      JOIN thread USING(upid)
+      JOIN thread_state USING(utid)
+      WHERE utid IN (${utids}) AND
+      thread_state.ts + thread_state.dur > ${toNs(area.startSec)} AND
+      thread_state.ts < ${toNs(area.endSec)}
+      GROUP BY utid, state`;
+
+    await engine.query(query);
+    return true;
+  }
+
+  getColumnDefinitions(): ColumnDef[] {
+    return [
+      {
+        title: 'Process',
+        kind: 'STRING',
+        columnConstructor: Uint16Array,
+        columnId: 'process_name',
+      },
+      {
+        title: 'PID',
+        kind: 'NUMBER',
+        columnConstructor: Uint16Array,
+        columnId: 'pid'
+      },
+      {
+        title: 'Thread',
+        kind: 'STRING',
+        columnConstructor: Uint16Array,
+        columnId: 'thread_name'
+      },
+      {
+        title: 'TID',
+        kind: 'NUMBER',
+        columnConstructor: Uint16Array,
+        columnId: 'tid'
+      },
+      {
+        title: 'State',
+        kind: 'STATE',
+        columnConstructor: Uint16Array,
+        columnId: 'state'
+      },
+      {
+        title: 'Wall duration (ms)',
+        kind: 'TIMESTAMP_NS',
+        columnConstructor: Float64Array,
+        columnId: 'total_dur'
+      },
+      {
+        title: 'Avg Wall duration (ms)',
+        kind: 'TIMESTAMP_NS',
+        columnConstructor: Float64Array,
+        columnId: 'avg_dur'
+      },
+      {
+        title: 'Occurrences',
+        kind: 'NUMBER',
+        columnConstructor: Uint16Array,
+        columnId: 'occurrences'
+      }
+    ];
+  }
+
+  getTabName() {
+    return 'Thread States';
+  }
+
+  getDefaultSorting(): Sorting {
+    return {column: 'total_dur', direction: 'DESC'};
+  }
+}
diff --git a/ui/src/controller/aggregation_controller.ts b/ui/src/controller/aggregation_controller.ts
deleted file mode 100644
index cdbb44f..0000000
--- a/ui/src/controller/aggregation_controller.ts
+++ /dev/null
@@ -1,118 +0,0 @@
-// 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 {AggregateCpuData} from '../common/aggregation_data';
-import {Engine} from '../common/engine';
-import {TimestampedAreaSelection} from '../common/state';
-import {toNs} from '../common/time';
-
-import {Controller} from './controller';
-import {globals} from './globals';
-
-export interface AggregationControllerArgs {
-  engine: Engine;
-}
-
-export class AggregationController extends Controller<'main'> {
-  private previousArea: TimestampedAreaSelection = {lastUpdate: 0};
-  private requestingData = false;
-  private queuedRequest = false;
-  constructor(private args: AggregationControllerArgs) {
-    super('main');
-  }
-
-  run() {
-    const selectedArea = globals.state.frontendLocalState.selectedArea;
-    const area = selectedArea.area;
-    if (!area ||
-        this.previousArea &&
-            this.previousArea.lastUpdate >= selectedArea.lastUpdate) {
-      return;
-    }
-    if (this.requestingData) {
-      this.queuedRequest = true;
-    } else {
-      this.requestingData = true;
-      Object.assign(this.previousArea, selectedArea);
-
-      this.args.engine.getCpus().then(cpusInTrace => {
-        const selectedCpuTracks =
-            cpusInTrace.filter(x => area.tracks.includes((x + 1).toString()));
-
-        const query =
-            `SELECT process.name, pid, thread.name, tid, sum(dur) AS total_dur,
-        count(1)
-        FROM process
-        JOIN thread USING(upid)
-        JOIN thread_state USING(utid)
-        WHERE cpu IN (${selectedCpuTracks}) AND
-        state = "Running" AND
-        thread_state.ts + thread_state.dur > ${toNs(area.startSec)} AND
-        thread_state.ts < ${toNs(area.endSec)}
-        GROUP BY utid ORDER BY total_dur DESC`;
-
-        this.args.engine.query(query)
-            .then(result => {
-              if (globals.state.frontendLocalState.selectedArea.lastUpdate >
-                  selectedArea.lastUpdate) {
-                return;
-              }
-
-              const numRows = +result.numRecords;
-              const data: AggregateCpuData = {
-                strings: [],
-                procNameId: new Uint16Array(numRows),
-                pid: new Uint32Array(numRows),
-                threadNameId: new Uint16Array(numRows),
-                tid: new Uint32Array(numRows),
-                totalDur: new Float64Array(numRows),
-                occurrences: new Uint16Array(numRows)
-              };
-
-              const stringIndexes = new Map<string, number>();
-              function internString(str: string) {
-                let idx = stringIndexes.get(str);
-                if (idx !== undefined) return idx;
-                idx = data.strings.length;
-                data.strings.push(str);
-                stringIndexes.set(str, idx);
-                return idx;
-              }
-
-              for (let row = 0; row < numRows; row++) {
-                const cols = result.columns;
-                data.procNameId[row] = internString(cols[0].stringValues![row]);
-                data.pid[row] = cols[1].longValues![row] as number;
-                data.threadNameId[row] =
-                    internString(cols[2].stringValues![row]);
-                data.tid[row] = cols[3].longValues![row] as number;
-                data.totalDur[row] = cols[4].longValues![row] as number;
-                data.occurrences[row] = cols[5].longValues![row] as number;
-              }
-              globals.publish('AggregateCpuData', data);
-            })
-            .catch(reason => {
-              console.error(reason);
-            })
-            .finally(() => {
-              this.requestingData = false;
-              if (this.queuedRequest) {
-                this.queuedRequest = false;
-                this.run();
-              }
-            });
-      });
-    }
-  }
-}
\ No newline at end of file
diff --git a/ui/src/controller/globals.ts b/ui/src/controller/globals.ts
index e9d3652..7e99ab6 100644
--- a/ui/src/controller/globals.ts
+++ b/ui/src/controller/globals.ts
@@ -23,7 +23,7 @@
 type PublishKinds = 'OverviewData'|'TrackData'|'Threads'|'QueryResult'|
     'LegacyTrace'|'SliceDetails'|'CounterDetails'|'HeapProfileDetails'|
     'HeapProfileFlamegraph'|'FileDownload'|'Loading'|'Search'|'BufferUsage'|
-    'RecordingLog'|'SearchResult'|'AggregateCpuData';
+    'RecordingLog'|'SearchResult'|'AggregateData';
 
 export interface App {
   state: State;
diff --git a/ui/src/controller/heap_profile_controller.ts b/ui/src/controller/heap_profile_controller.ts
index eadb704..ea7357f 100644
--- a/ui/src/controller/heap_profile_controller.ts
+++ b/ui/src/controller/heap_profile_controller.ts
@@ -35,15 +35,51 @@
 }
 const MIN_PIXEL_DISPLAYED = 1;
 
+class TablesCache {
+  private engine: Engine;
+  private cache: Map<string, string>;
+  private prefix: string;
+  private tableId: number;
+  private cacheSizeLimit: number;
+
+  constructor(engine: Engine, prefix: string) {
+    this.engine = engine;
+    this.cache = new Map<string, string>();
+    this.prefix = prefix;
+    this.tableId = 0;
+    this.cacheSizeLimit = 10;
+  }
+
+  async getTableName(query: string): Promise<string> {
+    let tableName = this.cache.get(query);
+    if (tableName === undefined) {
+      // TODO(hjd): This should be LRU.
+      if (this.cache.size > this.cacheSizeLimit) {
+        for (const name of this.cache.values()) {
+          await this.engine.query(`drop table ${name}`);
+        }
+        this.cache.clear();
+      }
+      tableName = `${this.prefix}_${this.tableId++}`;
+      await this.engine.query(
+          `create temp table if not exists ${tableName} as ${query}`);
+      this.cache.set(query, tableName);
+    }
+    return tableName;
+  }
+}
+
 export class HeapProfileController extends Controller<'main'> {
   private flamegraphDatasets: Map<string, CallsiteInfo[]> = new Map();
   private lastSelectedHeapProfile?: HeapProfileFlamegraph;
   private requestingData = false;
   private queuedRequest = false;
   private heapProfileDetails: HeapProfileDetails = {};
+  private cache: TablesCache;
 
   constructor(private args: HeapProfileControllerArgs) {
     super('main');
+    this.cache = new TablesCache(args.engine, 'grouped_callsites');
   }
 
   run() {
@@ -60,12 +96,21 @@
             this.copyHeapProfile(selection);
 
         this.getHeapProfileMetadata(
-                selectedHeapProfile.ts, selectedHeapProfile.upid)
+                selection.type,
+                selectedHeapProfile.ts,
+                selectedHeapProfile.upid)
             .then(result => {
               if (result !== undefined) {
                 Object.assign(this.heapProfileDetails, result);
               }
 
+              // TODO(hjd): Clean this up.
+              if (this.lastSelectedHeapProfile &&
+                  this.lastSelectedHeapProfile.focusRegex !==
+                      selection.focusRegex) {
+                this.flamegraphDatasets.clear();
+              }
+
               this.lastSelectedHeapProfile = this.copyHeapProfile(selection);
 
               const expandedId = selectedHeapProfile.expandedCallsite ?
@@ -85,7 +130,9 @@
                           selectedHeapProfile.viewingOption :
                           DEFAULT_VIEWING_OPTION,
                       selection.ts,
-                      selectedHeapProfile.upid)
+                      selectedHeapProfile.upid,
+                      selectedHeapProfile.type,
+                      selectedHeapProfile.focusRegex)
                   .then(flamegraphData => {
                     if (flamegraphData !== undefined && selection &&
                         selection.kind === selectedHeapProfile.kind &&
@@ -119,8 +166,10 @@
       id: heapProfile.id,
       upid: heapProfile.upid,
       ts: heapProfile.ts,
+      type: heapProfile.type,
       expandedCallsite: heapProfile.expandedCallsite,
-      viewingOption: heapProfile.viewingOption
+      viewingOption: heapProfile.viewingOption,
+      focusRegex: heapProfile.focusRegex,
     };
   }
 
@@ -130,9 +179,11 @@
          (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 ||
+           this.lastSelectedHeapProfile.focusRegex !== selection.focusRegex ||
            this.lastSelectedHeapProfile.expandedCallsite !==
                selection.expandedCallsite)));
   }
@@ -151,8 +202,8 @@
 
 
   async getFlamegraphData(
-      baseKey: string, viewingOption: string, ts: number,
-      upid: number): Promise<CallsiteInfo[]> {
+      baseKey: string, viewingOption: string, ts: number, upid: number,
+      type: string, focusRegex: string): Promise<CallsiteInfo[]> {
     let currentData: CallsiteInfo[];
     const key = `${baseKey}-${viewingOption}`;
     if (this.flamegraphDatasets.has(key)) {
@@ -163,7 +214,8 @@
       // 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, focusRegex);
       currentData =
           await this.getFlamegraphDataFromTables(tableName, viewingOption);
       this.flamegraphDatasets.set(key, currentData);
@@ -175,78 +227,98 @@
       tableName: string, viewingOption = DEFAULT_VIEWING_OPTION) {
     let orderBy = '';
     let sizeIndex = 4;
+    let selfIndex = 9;
+    // 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 cumulative_size > 0 order by depth, parent_id,
+        orderBy = `where cumulative_size > 0 and depth < ${
+            maxDepth} order by depth, parent_id,
             cumulative_size desc, name`;
         sizeIndex = 4;
+        selfIndex = 9;
         break;
       case ALLOC_SPACE_MEMORY_ALLOCATED_KEY:
-        orderBy = `where cumulative_alloc_size > 0 order by depth, parent_id,
+        orderBy = `where cumulative_alloc_size > 0 and depth < ${
+            maxDepth} order by depth, parent_id,
             cumulative_alloc_size desc, name`;
         sizeIndex = 5;
+        selfIndex = 9;
         break;
       case OBJECTS_ALLOCATED_NOT_FREED_KEY:
-        orderBy = `where cumulative_count > 0 order by depth, parent_id,
+        orderBy = `where cumulative_count > 0 and depth < ${
+            maxDepth} order by depth, parent_id,
             cumulative_count desc, name`;
         sizeIndex = 6;
+        selfIndex = 10;
         break;
       case OBJECTS_ALLOCATED_KEY:
-        orderBy = `where cumulative_alloc_count > 0 order by depth, parent_id,
+        orderBy = `where cumulative_alloc_count > 0 and depth < ${
+            maxDepth} order by depth, parent_id,
             cumulative_alloc_count desc, name`;
         sizeIndex = 7;
+        selfIndex = 10;
         break;
       default:
         break;
     }
 
     const callsites = await this.args.engine.query(
-        `SELECT id, name, parent_id, depth, cumulative_size,
-        cumulative_alloc_size, cumulative_count,
-        cumulative_alloc_count, map_name, 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, count 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];
       const mapping = callsites.columns[8].stringValues![i];
-      const selfSize = +callsites.columns[9].longValues![i];
+      const selfSize = +callsites.columns[selfIndex].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
       // will stay unique.
-      flamegraphData.push(
-          {id: i, totalSize, depth, parentId, name, selfSize, mapping});
+      flamegraphData.push({
+        id: i,
+        totalSize,
+        depth,
+        parentId,
+        name,
+        selfSize,
+        mapping,
+        merged: false
+      });
     }
     return flamegraphData;
   }
 
-  private async prepareViewsAndTables(ts: number, upid: number):
-      Promise<string> {
+  private async prepareViewsAndTables(
+      ts: number, upid: number, type: string,
+      focusRegex: string): Promise<string> {
     // Creating unique names for views so we can reuse and not delete them
     // for each marker.
-    const tableNameGroupedCallsitesForFlamegraph =
-        this.tableName(`grouped_callsites_for_flamegraph`);
+    let whereClause = '';
+    if (focusRegex !== '') {
+      whereClause = `where focus_str = '${focusRegex}'`;
+    }
 
-    await this.args.engine.query(`create temp table if not exists ${
-        tableNameGroupedCallsitesForFlamegraph} as
-        select id, name, map_name, parent_id, depth, cumulative_size,
+    return this.cache.getTableName(
+        `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}, 'native')`);
-    return tableNameGroupedCallsitesForFlamegraph;
-  }
-
-  tableName(name: string): string {
-    const selection = globals.state.currentHeapProfileFlamegraph;
-    if (!selection) return name;
-    return `${name}_${selection.upid}_${selection.ts}`;
+          from experimental_flamegraph(${ts}, ${upid}, '${type}') ${
+            whereClause}`);
   }
 
   getMinSizeDisplayed(flamegraphData: CallsiteInfo[], rootSize?: number):
@@ -260,7 +332,7 @@
     return MIN_PIXEL_DISPLAYED * rootSize / width;
   }
 
-  async getHeapProfileMetadata(ts: number, upid: number) {
+  async getHeapProfileMetadata(type: string, ts: number, upid: number) {
     // Don't do anything if selection of the marker stayed the same.
     if ((this.lastSelectedHeapProfile !== undefined &&
          ((this.lastSelectedHeapProfile.ts === ts &&
@@ -273,15 +345,7 @@
     const pidValue = await this.args.engine.query(
         `select pid from process where upid = ${upid}`);
     const pid = pidValue.columns[0].longValues![0];
-    const allocatedMemory = await this.args.engine.query(
-        `select sum(size) from heap_profile_allocation where ts <= ${
-            ts} and size > 0 and upid = ${upid}`);
-    const allocated = allocatedMemory.columns[0].longValues![0];
-    const allocatedNotFreedMemory = await this.args.engine.query(
-        `select sum(size) from heap_profile_allocation where ts <= ${
-            ts} and upid = ${upid}`);
-    const allocatedNotFreed = allocatedNotFreedMemory.columns[0].longValues![0];
     const startTime = fromNs(ts) - globals.state.traceTime.startSec;
-    return {ts: startTime, allocated, allocatedNotFreed, tsNs: ts, pid, upid};
+    return {ts: startTime, tsNs: ts, pid, upid, type};
   }
 }
diff --git a/ui/src/controller/query_controller.ts b/ui/src/controller/query_controller.ts
index dd1a726..d1c8314 100644
--- a/ui/src/controller/query_controller.ts
+++ b/ui/src/controller/query_controller.ts
@@ -55,7 +55,7 @@
 
   private async runQuery(sqlQuery: string) {
     const startMs = performance.now();
-    const rawResult = await this.args.engine.query(sqlQuery);
+    const rawResult = await this.args.engine.query(sqlQuery, true);
     const durationMs = performance.now() - startMs;
     const columns = rawQueryResultColumns(rawResult);
     const rows =
diff --git a/ui/src/controller/record_controller.ts b/ui/src/controller/record_controller.ts
index 0b1063f..703b190 100644
--- a/ui/src/controller/record_controller.ts
+++ b/ui/src/controller/record_controller.ts
@@ -25,10 +25,12 @@
   BufferConfig,
   ChromeConfig,
   ConsumerPort,
-  ContinuousDumpConfig,
   DataSourceConfig,
   FtraceConfig,
   HeapprofdConfig,
+  JavaContinuousDumpConfig,
+  JavaHprofConfig,
+  NativeContinuousDumpConfig,
   ProcessStatsConfig,
   SysStatsConfig,
   TraceConfig,
@@ -185,8 +187,8 @@
   if (uiCfg.memHiFreq) {
     procThreadAssociationPolling = true;
     procThreadAssociationFtrace = true;
+    ftraceEvents.add('mm_event/mm_event_record');
     ftraceEvents.add('kmem/rss_stat');
-    ftraceEvents.add('kmem/mm_event');
     ftraceEvents.add('kmem/ion_heap_grow');
     ftraceEvents.add('kmem/ion_heap_shrink');
   }
@@ -227,29 +229,51 @@
   let heapprofd: HeapprofdConfig|undefined = undefined;
   if (uiCfg.heapProfiling) {
     // TODO(taylori): Check or inform user if buffer size are too small.
-    if (heapprofd === undefined) heapprofd = new HeapprofdConfig();
-    heapprofd.samplingIntervalBytes = uiCfg.hpSamplingIntervalBytes;
+    const cfg = new HeapprofdConfig();
+    cfg.samplingIntervalBytes = uiCfg.hpSamplingIntervalBytes;
     if (uiCfg.hpSharedMemoryBuffer >= 8192 &&
         uiCfg.hpSharedMemoryBuffer % 4096 === 0) {
-      heapprofd.shmemSizeBytes = uiCfg.hpSharedMemoryBuffer;
+      cfg.shmemSizeBytes = uiCfg.hpSharedMemoryBuffer;
     }
-    if (uiCfg.hpProcesses !== '') {
-      uiCfg.hpProcesses.split('\n').forEach(value => {
-        if (isNaN(+value)) {
-          heapprofd!.processCmdline.push(value);
-        } else {
-          heapprofd!.pid.push(+value);
-        }
-      });
+    for (const value of uiCfg.hpProcesses.split('\n')) {
+      if (value === '') {
+        // Ignore empty lines
+      } else if (isNaN(+value)) {
+        cfg.processCmdline.push(value);
+      } else {
+        cfg.pid.push(+value);
+      }
     }
     if (uiCfg.hpContinuousDumpsInterval > 0) {
-      heapprofd.continuousDumpConfig = new ContinuousDumpConfig();
-      heapprofd.continuousDumpConfig.dumpIntervalMs =
-          uiCfg.hpContinuousDumpsInterval;
-      heapprofd.continuousDumpConfig.dumpPhaseMs =
-          uiCfg.hpContinuousDumpsPhase > 0 ? uiCfg.hpContinuousDumpsPhase :
-                                             undefined;
+      const cdc = cfg.continuousDumpConfig = new NativeContinuousDumpConfig();
+      cdc.dumpIntervalMs = uiCfg.hpContinuousDumpsInterval;
+      if (uiCfg.hpContinuousDumpsPhase > 0) {
+        cdc.dumpPhaseMs = uiCfg.hpContinuousDumpsPhase;
+      }
     }
+    heapprofd = cfg;
+  }
+
+  let javaHprof: JavaHprofConfig|undefined = undefined;
+  if (uiCfg.javaHeapDump) {
+    const cfg = new JavaHprofConfig();
+    for (const value of uiCfg.jpProcesses.split('\n')) {
+      if (value === '') {
+        // Ignore empty lines
+      } else if (isNaN(+value)) {
+        cfg.processCmdline.push(value);
+      } else {
+        cfg.pid.push(+value);
+      }
+    }
+    if (uiCfg.jpContinuousDumpsInterval > 0) {
+      const cdc = cfg.continuousDumpConfig = new JavaContinuousDumpConfig();
+      cdc.dumpIntervalMs = uiCfg.jpContinuousDumpsInterval;
+      if (uiCfg.hpContinuousDumpsPhase > 0) {
+        cdc.dumpPhaseMs = uiCfg.jpContinuousDumpsPhase;
+      }
+    }
+    javaHprof = cfg;
   }
 
   if (uiCfg.procStats || procThreadAssociationPolling || trackInitialOomScore) {
@@ -384,6 +408,15 @@
     protoCfg.dataSources.push(ds);
   }
 
+  if (javaHprof !== undefined) {
+    const ds = new TraceConfig.DataSource();
+    ds.config = new DataSourceConfig();
+    ds.config.targetBuffer = 0;
+    ds.config.name = 'android.java_hprof';
+    ds.config.javaHprofConfig = javaHprof;
+    protoCfg.dataSources.push(ds);
+  }
+
   if (uiCfg.ftrace || uiCfg.atraceApps.length > 0 || ftraceEvents.size > 0 ||
       atraceCats.size > 0 || atraceApps.size > 0) {
     const ds = new TraceConfig.DataSource();
diff --git a/ui/src/controller/search_controller.ts b/ui/src/controller/search_controller.ts
index 5e29627..fb6d17f 100644
--- a/ui/src/controller/search_controller.ts
+++ b/ui/src/controller/search_controller.ts
@@ -205,7 +205,7 @@
 
     const rawResult = await this.query(`
     select
-      row_id as slice_id,
+      id as slice_id,
       ts,
       'cpu' as source,
       cpu as source_id,
@@ -262,10 +262,6 @@
 
   private async query(query: string) {
     const result = await this.engine.query(query);
-    if (result.error) {
-      console.error(`Query error "${query}": ${result.error}`);
-      throw new Error(`Query error "${query}": ${result.error}`);
-    }
     return result;
   }
 }
diff --git a/ui/src/controller/selection_controller.ts b/ui/src/controller/selection_controller.ts
index 540ce00..57c8ad8 100644
--- a/ui/src/controller/selection_controller.ts
+++ b/ui/src/controller/selection_controller.ts
@@ -37,12 +37,13 @@
     // TODO(taylori): Ideally thread_state should not be special cased, it
     // should have some form of id like everything else.
     if (selection.kind === 'THREAD_STATE') {
-      const sqlQuery = `SELECT row_id FROM sched WHERE utid = ${selection.utid}
+      const sqlQuery = `SELECT id FROM sched WHERE utid = ${selection.utid}
                         and ts = ${toNs(selection.ts)}`;
       this.args.engine.query(sqlQuery).then(result => {
-        const id = result.columns[0].longValues![0] as number;
-        this.sliceDetails(id);
+        if (result.columns[0].longValues!.length === 0) return;
+        this.sliceDetails(+result.columns[0].longValues![0]);
       });
+      this.lastSelectedKind = selection.kind;
       return;
     }
 
@@ -74,12 +75,11 @@
     } else if (selectedKind === 'SLICE') {
       this.sliceDetails(selectedId as number);
     } else if (selectedKind === 'CHROME_SLICE') {
-      if (selectedId === -1) {
-        globals.publish('SliceDetails', {ts: 0, name: 'Summarized slice'});
-        return;
-      }
-      const sqlQuery = `SELECT ts, dur, name, cat FROM slices
-      WHERE slice_id = ${selectedId}`;
+      const sqlQuery = `
+        SELECT ts, dur, name, cat, arg_set_id
+        FROM slice
+        WHERE id = ${selectedId}
+      `;
       this.args.engine.query(sqlQuery).then(result => {
         // Check selection is still the same on completion of query.
         const selection = globals.state.currentSelection;
@@ -90,18 +90,65 @@
           const name = result.columns[2].stringValues![0];
           const dur = fromNs(result.columns[1].longValues![0] as number);
           const category = result.columns[3].stringValues![0];
-          // TODO(nicomazz): Add arguments and thread timestamps
-          const selected: SliceDetails =
-              {ts: timeFromStart, dur, category, name, id: selectedId};
-          globals.publish('SliceDetails', selected);
+          const argId = result.columns[4].longValues![0] as number;
+          const argsAsync = this.getArgs(argId);
+          const descriptionAsync = this.describeSlice(selectedId);
+          Promise.all([argsAsync, descriptionAsync])
+              .then(([args, description]) => {
+                const selected: SliceDetails = {
+                  ts: timeFromStart,
+                  dur,
+                  category,
+                  name,
+                  id: selectedId,
+                  args,
+                  description,
+                };
+                globals.publish('SliceDetails', selected);
+              });
         }
       });
     }
   }
 
+  async describeSlice(id: number): Promise<Map<string, string>> {
+    const map = new Map<string, string>();
+    const query = `
+      select description, doc_link
+      from describe_slice
+      where slice_id = ${id}
+    `;
+    const result = await this.args.engine.query(query);
+    for (let i = 0; i < result.numRecords; i++) {
+      const description = result.columns[0].stringValues![i];
+      const docLink = result.columns[1].stringValues![i];
+      map.set('Description', description);
+      map.set('Documentation', docLink);
+    }
+    return map;
+  }
+
+  async getArgs(argId: number): Promise<Map<string, string>> {
+    const args = new Map<string, string>();
+    const query = `
+      select
+        flat_key AS name,
+        CAST(COALESCE(int_value, string_value, real_value) AS text) AS value
+      FROM args
+      WHERE arg_set_id = ${argId}
+    `;
+    const result = await this.args.engine.query(query);
+    for (let i = 0; i < result.numRecords; i++) {
+      const name = result.columns[0].stringValues![i];
+      const value = result.columns[1].stringValues![i];
+      args.set(name, value);
+    }
+    return args;
+  }
+
   async sliceDetails(id: number) {
     const sqlQuery = `SELECT ts, dur, priority, end_state, utid, cpu FROM sched
-    WHERE row_id = ${id}`;
+    WHERE id = ${id}`;
     this.args.engine.query(sqlQuery).then(result => {
       // Check selection is still the same on completion of query.
       const selection = globals.state.currentSelection;
@@ -115,24 +162,27 @@
         const cpu = result.columns[5].longValues![0] as number;
         const selected: SliceDetails =
             {ts: timeFromStart, dur, priority, endState, cpu, id, utid};
-        this.schedulingDetails(ts, utid).then(wakeResult => {
-          Object.assign(selected, wakeResult);
-          globals.publish('SliceDetails', selected);
-        });
+        this.schedulingDetails(ts, utid)
+            .then(wakeResult => {
+              Object.assign(selected, wakeResult);
+            })
+            .finally(() => {
+              globals.publish('SliceDetails', selected);
+            });
       }
     });
   }
 
   async counterDetails(ts: number, rightTs: number, id: number) {
     const counter = await this.args.engine.query(
-        `SELECT value FROM counter_values WHERE ts = ${ts} AND counter_id = ${
-            id}`);
+        `SELECT value, track_id FROM counter WHERE id = ${id}`);
     const value = counter.columns[0].doubleValues![0];
+    const trackId = counter.columns[1].longValues![0];
     // Finding previous value. If there isn't previous one, it will return 0 for
     // ts and value.
     const previous = await this.args.engine.query(
-        `SELECT MAX(ts), value FROM counter_values WHERE ts < ${
-            ts} and counter_id = ${id}`);
+        `SELECT MAX(ts), value FROM counter WHERE ts < ${ts} and track_id = ${
+            trackId}`);
     const previousValue = previous.columns[1].doubleValues![0];
     const endTs =
         rightTs !== -1 ? rightTs : toNs(globals.state.traceTime.endSec);
@@ -164,7 +214,8 @@
     const prevSchedRow = await this.args.engine.queryOneRow(queryPrevSched);
     // If this is the first sched slice for this utid or if the wakeup found
     // was after the previous slice then we know the wakeup was for this slice.
-    if (prevSchedRow[0] && wakeupRow[0] < prevSchedRow[0]) {
+    if (wakeupRow[0] === undefined ||
+        (prevSchedRow[0] !== undefined && wakeupRow[0] < prevSchedRow[0])) {
       return undefined;
     }
     const wakeupTs = wakeupRow[0];
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index c5b6130..912b7bf 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -49,7 +49,12 @@
 import {PROCESS_SUMMARY_TRACK} from '../tracks/process_summary/common';
 import {THREAD_STATE_TRACK_KIND} from '../tracks/thread_state/common';
 
-import {AggregationController} from './aggregation_controller';
+import {
+  CpuAggregationController
+} from './aggregation/cpu_aggregation_controller';
+import {
+  ThreadAggregationController
+} from './aggregation/thread_aggregation_controller';
 import {Child, Children, Controller} from './controller';
 import {globals} from './globals';
 import {
@@ -154,8 +159,14 @@
         const heapProfileArgs: HeapProfileControllerArgs = {engine};
         childControllers.push(
             Child('heapProfile', HeapProfileController, heapProfileArgs));
-        childControllers.push(
-            Child('aggregation', AggregationController, {engine}));
+        childControllers.push(Child(
+            'cpu_aggregation',
+            CpuAggregationController,
+            {engine, kind: 'cpu_aggregation'}));
+        childControllers.push(Child(
+            'thread_aggregation',
+            ThreadAggregationController,
+            {engine, kind: 'thread_state_aggregation'}));
         childControllers.push(Child('search', SearchController, {
           engine,
           app: globals,
@@ -268,6 +279,9 @@
 
     globals.dispatchMultiple(actions);
 
+    // Make sure the helper views are available before we start adding tracks.
+    await this.initaliseHelperViews();
+
     {
       // When we reload from a permalink don't create extra tracks:
       const {pinnedTracks, tracks} = globals.state;
@@ -278,7 +292,6 @@
 
     await this.listThreads();
     await this.loadTimelineOverview(traceTime);
-    await this.initaliseHelperViews();
     return engineMode;
   }
 
@@ -371,33 +384,35 @@
       }
     }
 
-
     const upidToProcessTracks = new Map();
     const rawProcessTracks = await engine.query(`
-      select
-        process_track.id as track_id,
-        process_track.upid,
-        process_track.name,
-        max(slice.depth) as max_depth
-      from process_track
-      join slice on slice.track_id = process_track.id
-      group by process_track.id
+      SELECT
+        pt.upid,
+        pt.name,
+        pt.track_ids,
+        MAX(experimental_slice_layout.layout_depth) as max_depth
+      FROM (
+        SELECT upid, name, GROUP_CONCAT(process_track.id) AS track_ids
+        FROM process_track GROUP BY upid, name
+      ) AS pt CROSS JOIN experimental_slice_layout
+      WHERE pt.track_ids = experimental_slice_layout.filter_track_ids
+      GROUP BY pt.track_ids;
     `);
     for (let i = 0; i < rawProcessTracks.numRecords; i++) {
-      const trackId = rawProcessTracks.columns[0].longValues![i];
-      const upid = rawProcessTracks.columns[1].longValues![i];
-      const name = rawProcessTracks.columns[2].stringValues![i];
-      const maxDepth = rawProcessTracks.columns[3].longValues![i];
+      const upid = +rawProcessTracks.columns[0].longValues![i];
+      const name = rawProcessTracks.columns[1].stringValues![i];
+      const rawTrackIds = rawProcessTracks.columns[2].stringValues![i];
+      const trackIds = rawTrackIds.split(',').map(v => Number(v));
+      const maxDepth = +rawProcessTracks.columns[3].longValues![i];
       const track = {
         engineId: this.engineId,
         kind: 'AsyncSliceTrack',
         name,
         config: {
-          trackId,
           maxDepth,
+          trackIds,
         },
       };
-
       const tracks = upidToProcessTracks.get(upid);
       if (tracks) {
         tracks.push(track);
@@ -407,7 +422,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++) {
@@ -500,41 +517,61 @@
     interface CounterTrack {
       name: string;
       trackId: number;
+      startTs?: number;
+      endTs?: number;
     }
 
     const counterUtids = new Map<number, CounterTrack[]>();
     const threadCounters = await engine.query(`
-      select name, utid, id
-      from thread_counter_track
+      select thread_counter_track.name, utid, thread_counter_track.id,
+      start_ts, end_ts from thread_counter_track join thread using(utid)
     `);
     for (let i = 0; i < threadCounters.numRecords; i++) {
       const name = threadCounters.columns[0].stringValues![i];
       const utid = +threadCounters.columns[1].longValues![i];
       const trackId = +threadCounters.columns[2].longValues![i];
+      let startTs = undefined;
+      let endTs = undefined;
+      if (!threadCounters.columns[3].isNulls![i]) {
+        startTs = +threadCounters.columns[3].longValues![i] / 1e9;
+      }
+      if (!threadCounters.columns[4].isNulls![i]) {
+        endTs = +threadCounters.columns[4].longValues![i] / 1e9;
+      }
 
+      const track: CounterTrack = {name, trackId, startTs, endTs};
       const el = counterUtids.get(utid);
       if (el === undefined) {
-        counterUtids.set(utid, [{name, trackId}]);
+        counterUtids.set(utid, [track]);
       } else {
-        el.push({name, trackId});
+        el.push(track);
       }
     }
 
     const counterUpids = new Map<number, CounterTrack[]>();
     const processCounters = await engine.query(`
-      select name, upid, id
-      from process_counter_track
+      select process_counter_track.name, upid, process_counter_track.id,
+      start_ts, end_ts from process_counter_track join process using(upid)
     `);
     for (let i = 0; i < processCounters.numRecords; i++) {
       const name = processCounters.columns[0].stringValues![i];
       const upid = +processCounters.columns[1].longValues![i];
       const trackId = +processCounters.columns[2].longValues![i];
+      let startTs = undefined;
+      let endTs = undefined;
+      if (!processCounters.columns[3].isNulls![i]) {
+        startTs = +processCounters.columns[3].longValues![i] / 1e9;
+      }
+      if (!processCounters.columns[4].isNulls![i]) {
+        endTs = +processCounters.columns[4].longValues![i] / 1e9;
+      }
 
+      const track: CounterTrack = {name, trackId, startTs, endTs};
       const el = counterUpids.get(upid);
       if (el === undefined) {
-        counterUpids.set(upid, [{name, trackId}]);
+        counterUpids.set(upid, [track]);
       } else {
-        el.push({name, trackId});
+        el.push(track);
       }
     }
 
@@ -640,21 +677,35 @@
           config: {pidForColor, upid, utid},
         });
 
-        const name = upid === null ?
-            `${threadName} ${tid}` :
-            `${
-                processName === null && heapUpids.has(upid) ?
-                    'Heap Profile for' :
-                    processName} ${pid}`;
-        addTrackGroupActions.push(Actions.addTrackGroup({
+        const name =
+            getTrackName({utid, processName, pid, threadName, tid, upid});
+        const addTrackGroup = Actions.addTrackGroup({
           engineId: this.engineId,
           summaryTrackId,
           name,
           id: pUuid,
           collapsed: !(upid !== null && heapUpids.has(upid)),
-        }));
+        });
+
+        // If the track group contains a heap profile, it should be before all
+        // other processes.
+        if (upid !== null && heapUpids.has(upid)) {
+          addTrackGroupActions.unshift(addTrackGroup);
+        } else {
+          addTrackGroupActions.push(addTrackGroup);
+        }
 
         if (upid !== null) {
+          if (heapUpids.has(upid)) {
+            tracksToAdd.push({
+              engineId: this.engineId,
+              kind: HEAP_PROFILE_TRACK_KIND,
+              name: `Heap Profile`,
+              trackGroup: pUuid,
+              config: {upid}
+            });
+          }
+
           const counterNames = counterUpids.get(upid);
           if (counterNames !== undefined) {
             counterNames.forEach(element => {
@@ -663,21 +714,16 @@
                 kind: 'CounterTrack',
                 name: element.name,
                 trackGroup: pUuid,
-                config: {name: element.name, trackId: element.trackId}
+                config: {
+                  name: element.name,
+                  trackId: element.trackId,
+                  startTs: element.startTs,
+                  endTs: element.endTs,
+                }
               });
             });
           }
 
-          if (heapUpids.has(upid)) {
-            tracksToAdd.push({
-              engineId: this.engineId,
-              kind: HEAP_PROFILE_TRACK_KIND,
-              name: `Heap Profile`,
-              trackGroup: pUuid,
-              config: {upid}
-            });
-          }
-
           if (upidToProcessTracks.has(upid)) {
             for (const track of upidToProcessTracks.get(upid)) {
               tracksToAdd.push(Object.assign(track, {trackGroup: pUuid}));
@@ -696,6 +742,8 @@
             config: {
               name: element.name,
               trackId: element.trackId,
+              startTs: element.startTs,
+              endTs: element.endTs,
             }
           });
         });
@@ -704,7 +752,7 @@
         tracksToAdd.push({
           engineId: this.engineId,
           kind: THREAD_STATE_TRACK_KIND,
-          name: `${threadName} [${tid}]`,
+          name: getTrackName({utid, tid, threadName}),
           trackGroup: pUuid,
           config: {utid}
         });
@@ -714,12 +762,10 @@
         tracksToAdd.push({
           engineId: this.engineId,
           kind: SLICE_TRACK_KIND,
-          name: `${threadName} [${tid}]`,
+          name: getTrackName({utid, tid, threadName}),
           trackGroup: pUuid,
-          config: {
-            maxDepth: threadTrack.maxDepth,
-            trackId: threadTrack.trackId
-          },
+          config:
+              {maxDepth: threadTrack.maxDepth, trackId: threadTrack.trackId},
         });
       }
     }
@@ -735,6 +781,42 @@
       });
     }
 
+    const annotationSliceRows = await engine.query(`
+      SELECT id, name FROM annotation_slice_track`);
+    for (let i = 0; i < annotationSliceRows.numRecords; i++) {
+      const id = annotationSliceRows.columns[0].longValues![i];
+      const name = annotationSliceRows.columns[1].stringValues![i];
+      tracksToAdd.push({
+        engineId: this.engineId,
+        kind: SLICE_TRACK_KIND,
+        name,
+        trackGroup: SCROLLING_TRACK_GROUP,
+        config: {
+          maxDepth: 0,
+          namespace: 'annotation',
+          trackId: id,
+        },
+      });
+    }
+
+    const annotationCounterRows = await engine.query(`
+      SELECT id, name FROM annotation_counter_track`);
+    for (let i = 0; i < annotationCounterRows.numRecords; i++) {
+      const id = annotationCounterRows.columns[0].longValues![i];
+      const name = annotationCounterRows.columns[1].stringValues![i];
+      tracksToAdd.push({
+        engineId: this.engineId,
+        kind: 'CounterTrack',
+        name,
+        trackGroup: SCROLLING_TRACK_GROUP,
+        config: {
+          name,
+          namespace: 'annotation',
+          trackId: id,
+        }
+      });
+    }
+
     addTrackGroupActions.push(Actions.addTracks({tracks: tracksToAdd}));
     globals.dispatchMultiple(addTrackGroupActions);
   }
@@ -804,7 +886,7 @@
          from thread
          inner join (
            select
-             cast((ts - ${traceStartNs})/${stepSecNs} as int) as bucket
+             cast((ts - ${traceStartNs})/${stepSecNs} as int) as bucket,
              sum(dur) as utid_sum,
              utid
            from slice
@@ -887,12 +969,108 @@
       select ts, dur, utid, cpu,
       case when end_state is not null then 'Running'
       when lag(end_state) over ordered is not null
-      then lag(end_state) over ordered else 'Runnable'
+      then lag(end_state) over ordered else 'R'
       end as state
       from thread_span window ordered as
       (partition by utid order by ts)`);
 
     await engine.query(`create index utid_index on thread_state(utid)`);
+
+    // Create the helper tables for all the annotations related data.
+    await engine.query(`
+      CREATE TABLE annotation_counter_track(
+        id INTEGER PRIMARY KEY,
+        name STRING,
+        __metric_name STRING
+      );
+    `);
+    await engine.query(`
+      CREATE TABLE annotation_slice_track(
+        id INTEGER PRIMARY KEY,
+        name STRING,
+        __metric_name STRING
+      );
+    `);
+
+    await engine.query(`
+      CREATE TABLE annotation_counter(
+        id BIG INT,
+        track_id INT,
+        ts BIG INT,
+        value DOUBLE,
+        PRIMARY KEY (track_id, ts)
+      ) WITHOUT ROWID;
+    `);
+    await engine.query(`
+      CREATE TABLE annotation_slice(
+        id BIG INT,
+        track_id INT,
+        ts BIG INT,
+        dur BIG INT,
+        depth INT,
+        name STRING,
+        PRIMARY KEY (track_id, ts)
+      ) WITHOUT ROWID;
+    `);
+
+    for (const metric of ['android_startup', 'android_ion']) {
+      // We don't care about the actual result of metric here as we are just
+      // interested in the annotation tracks.
+      const metricResult = await engine.computeMetric([metric]);
+      assertTrue(metricResult.error.length === 0);
+
+      const result = await engine.query(`
+        SELECT * FROM ${metric}_annotations LIMIT 1`);
+
+      const hasSliceName =
+          result.columnDescriptors.some(x => x.name === 'slice_name');
+      const hasDur = result.columnDescriptors.some(x => x.name === 'dur');
+
+      if (hasSliceName && hasDur) {
+        await engine.query(`
+          INSERT INTO annotation_slice_track(name, __metric_name)
+          SELECT DISTINCT track_name, '${metric}' as metric_name
+          FROM ${metric}_annotations
+          WHERE track_type = 'slice'
+        `);
+        await engine.query(`
+          INSERT INTO annotation_slice(id, track_id, ts, dur, depth, name)
+          SELECT
+            -1 as id,
+            t.id AS track_id,
+            ts,
+            dur,
+            0 AS depth,
+            slice_name AS name
+          FROM ${metric}_annotations a
+          JOIN annotation_slice_track t
+          ON a.track_name = t.name AND t.__metric_name = '${metric}'
+          ORDER BY t.id, ts
+        `);
+      }
+
+      const hasValue = result.columnDescriptors.some(x => x.name === 'value');
+      if (hasValue) {
+        await engine.query(`
+          INSERT INTO annotation_counter_track(name, __metric_name)
+          SELECT DISTINCT track_name, '${metric}' as metric_name
+          FROM ${metric}_annotations
+          WHERE track_type = 'counter'
+        `);
+        await engine.query(`
+          INSERT INTO annotation_counter(id, track_id, ts, value)
+          SELECT
+            -1 as id,
+            t.id AS track_id,
+            ts,
+            value
+          FROM ${metric}_annotations a
+          JOIN annotation_counter_track t
+          ON a.track_name = t.name AND t.__metric_name = '${metric}'
+          ORDER BY t.id, ts
+        `);
+      }
+    }
   }
 
   private updateStatus(msg: string): void {
@@ -902,3 +1080,36 @@
     }));
   }
 }
+
+function getTrackName(args: Partial<{
+  utid: number,
+  processName: string | null,
+  pid: number | null,
+  threadName: string | null,
+  tid: number | null,
+  upid: number | null
+}>) {
+  const {upid, utid, processName, threadName, pid, tid} = args;
+
+  const hasUpid = upid !== undefined && upid !== null;
+  const hasUtid = utid !== undefined && utid !== null;
+  const hasProcessName = processName !== undefined && processName !== null;
+  const hasThreadName = threadName !== undefined && threadName !== null;
+  const hasTid = tid !== undefined && tid !== null;
+  const hasPid = pid !== undefined && pid !== null;
+
+  if (hasUpid && hasPid && hasProcessName) {
+    return `${processName} ${pid}`;
+  } else if (hasUpid && hasPid) {
+    return `Process ${pid}`;
+  } else if (hasThreadName && hasTid) {
+    return `${threadName} ${tid}`;
+  } else if (hasTid) {
+    return `Thread ${tid}`;
+  } else if (hasUpid) {
+    return `upid: ${upid}`;
+  } else if (hasUtid) {
+    return `utid: ${utid}`;
+  }
+  return 'Unknown';
+}
diff --git a/ui/src/controller/track_controller.ts b/ui/src/controller/track_controller.ts
index d036d62..623d3e0 100644
--- a/ui/src/controller/track_controller.ts
+++ b/ui/src/controller/track_controller.ts
@@ -22,12 +22,15 @@
 import {ControllerFactory} from './controller';
 import {globals} from './globals';
 
+interface TrackConfig {}
+
+type TrackConfigWithNamespace = TrackConfig&{namespace: string};
+
 
 // TrackController is a base class overridden by track implementations (e.g.,
 // sched slices, nestable slices, counters).
-export abstract class TrackController<Config = {},
-                                      Data extends TrackData = TrackData>
-    extends Controller<'main'> {
+export abstract class TrackController<
+    Config, Data extends TrackData = TrackData> extends Controller<'main'> {
   readonly trackId: string;
   readonly engine: Engine;
   private data?: TrackData;
@@ -54,6 +57,18 @@
     return this.trackState.config as Config;
   }
 
+  configHasNamespace(config: TrackConfig): config is TrackConfigWithNamespace {
+    return 'namespace' in config;
+  }
+
+  namespaceTable(tableName: string): string {
+    if (this.configHasNamespace(this.config)) {
+      return this.config.namespace + '_' + tableName;
+    } else {
+      return tableName;
+    }
+  }
+
   publish(data: Data): void {
     this.data = data;
     globals.publish('TrackData', {id: this.trackId, data});
@@ -78,10 +93,6 @@
 
   protected async query(query: string) {
     const result = await this.engine.query(query);
-    if (result.error) {
-      console.error(`Query error "${query}": ${result.error}`);
-      throw new Error(`Query error "${query}": ${result.error}`);
-    }
     return result;
   }
 
@@ -109,7 +120,10 @@
 
   run() {
     const visibleState = globals.state.frontendLocalState.visibleState;
-    if (visibleState === undefined) return;
+    if (visibleState === undefined || visibleState.resolution === undefined ||
+        visibleState.resolution === Infinity) {
+      return;
+    }
     const dur = visibleState.endSec - visibleState.startSec;
     if (globals.state.visibleTracks.includes(this.trackId) &&
         this.shouldRequestData(visibleState)) {
diff --git a/ui/src/frontend/aggregation_panel.ts b/ui/src/frontend/aggregation_panel.ts
index 67f7f8f..fdcea94 100644
--- a/ui/src/frontend/aggregation_panel.ts
+++ b/ui/src/frontend/aggregation_panel.ts
@@ -14,52 +14,79 @@
 
 import * as m from 'mithril';
 
-import {AggregateCpuData} from '../common/aggregation_data';
+import {Actions} from '../common/actions';
+import {AggregateData, Column} from '../common/aggregation_data';
+import {translateState} from '../common/thread_state';
 
 import {globals} from './globals';
 import {Panel} from './panel';
 
-export class AggregationPanel extends Panel {
-  view() {
-    const data = globals.aggregateCpuData;
+export interface AggregationPanelAttrs {
+  data: AggregateData;
+  kind: string;
+}
+
+export class AggregationPanel extends Panel<AggregationPanelAttrs> {
+  view({attrs}: m.CVnode<AggregationPanelAttrs>) {
     return m(
         '.details-panel',
         m('.details-panel-heading.aggregation',
           m('table',
             m('tr',
-              m('th', 'Process'),
-              m('th', 'Thread'),
-              m('th', 'Wall duration (ms)'),
-              m('th', 'Avg. Wall duration (ms)'),
-              m('th', 'Occurrences')))),
+              attrs.data.columns.map(
+                  col => this.formatColumnHeading(col, attrs.kind))))),
         m(
             '.details-table.aggregation',
-            m('table', this.getRows(data)),
+            m('table', this.getRows(attrs.data)),
             ));
   }
 
-  getRows(data: AggregateCpuData) {
-    if (!data.strings || !data.procNameId || !data.threadNameId || !data.pid ||
-        !data.tid || !data.totalDur || !data.occurrences) {
-      return;
+  formatColumnHeading(col: Column, id: string) {
+    const pref = globals.state.aggregatePreferences[id];
+    let sortIcon = '';
+    if (pref && pref.sorting && pref.sorting.column === col.columnId) {
+      sortIcon = pref.sorting.direction === 'DESC' ? 'arrow_drop_down' :
+                                                     'arrow_drop_up';
     }
+    return m(
+        'th',
+        {
+          onclick: () => {
+            globals.dispatch(
+                Actions.updateAggregateSorting({id, column: col.columnId}));
+          }
+        },
+        col.title,
+        m('i.material-icons', sortIcon));
+  }
+
+  getRows(data: AggregateData) {
+    if (data.columns.length === 0) return;
     const rows = [];
-    for (let i = 0; i < data.pid.length; i++) {
-      const row =
-          [m('tr',
-             m('td', `${data.strings[data.procNameId[i]]} [${data.pid[i]}]`),
-             m('td', `${data.strings[data.threadNameId[i]]} [${data.tid[i]}]`),
-             m('td', `${data.totalDur[i] / 1000000}`),
-             m('td',
-               `${
-                   +
-                   (data.totalDur[i] / data.occurrences[i] / 1000000)
-                       .toFixed(6)}`),
-             m('td', `${data.occurrences[i]}`))];
-      rows.push(row);
+    for (let i = 0; i < data.columns[0].data.length; i++) {
+      const row = [];
+      for (let j = 0; j < data.columns.length; j++) {
+        row.push(m('td', this.getFormattedData(data, i, j)));
+      }
+      rows.push(m('tr', row));
     }
     return rows;
   }
 
+  getFormattedData(data: AggregateData, rowIndex: number, columnIndex: number) {
+    switch (data.columns[columnIndex].kind) {
+      case 'STRING':
+        return `${data.strings[data.columns[columnIndex].data[rowIndex]]}`;
+      case 'TIMESTAMP_NS':
+        return `${data.columns[columnIndex].data[rowIndex] / 1000000}`;
+      case 'STATE':
+        return translateState(
+            `${data.strings[data.columns[columnIndex].data[rowIndex]]}`);
+      case 'NUMBER':
+      default:
+        return `${data.columns[columnIndex].data[rowIndex]}`;
+    }
+  }
+
   renderCanvas() {}
-}
+}
\ No newline at end of file
diff --git a/ui/src/frontend/chrome_slice_panel.ts b/ui/src/frontend/chrome_slice_panel.ts
index 68c7fb0..9931a2a 100644
--- a/ui/src/frontend/chrome_slice_panel.ts
+++ b/ui/src/frontend/chrome_slice_panel.ts
@@ -22,27 +22,30 @@
 export class ChromeSliceDetailsPanel extends Panel {
   view() {
     const sliceInfo = globals.sliceDetails;
-    if (sliceInfo.ts && sliceInfo.dur && sliceInfo.name) {
+    if (sliceInfo.ts !== undefined && sliceInfo.dur !== undefined &&
+        sliceInfo.name !== undefined) {
       return m(
           '.details-panel',
           m('.details-panel-heading', m('h2', `Slice Details`)),
           m(
               '.details-table',
-              [m('table.half-width',
-                 [
-                   m('tr', m('th', `Name`), m('td', `${sliceInfo.name}`)),
-                   (sliceInfo.category === '[NULL]') ?
-                       null :
-                       m('tr',
-                         m('th', `Category`),
-                         m('td', `${sliceInfo.category}`)),
-                   m('tr',
-                     m('th', `Start time`),
-                     m('td', `${timeToCode(sliceInfo.ts)}`)),
-                   m('tr',
-                     m('th', `Duration`),
-                     m('td', `${timeToCode(sliceInfo.dur)}`))
-                 ])],
+              m('table.half-width',
+                m('tr', m('th', `Name`), m('td', `${sliceInfo.name}`)),
+                m('tr',
+                  m('th', `Category`),
+                  m('td',
+                    `${
+                        sliceInfo.category === '[NULL]' ?
+                            'N/A' :
+                            sliceInfo.category}`)),
+                m('tr',
+                  m('th', `Start time`),
+                  m('td', `${timeToCode(sliceInfo.ts)}`)),
+                m('tr',
+                  m('th', `Duration`),
+                  m('td', `${timeToCode(sliceInfo.dur)}`)),
+                this.getDescription(sliceInfo.description)),
+              this.getArgs(sliceInfo.args),
               ));
     } else {
       return m(
@@ -54,5 +57,24 @@
                 )));
     }
   }
+
   renderCanvas(_ctx: CanvasRenderingContext2D, _size: PanelSize) {}
+
+  getArgs(args?: Map<string, string>): m.Vnode[] {
+    if (!args || args.size === 0) return [];
+    const result = [m('tr', m('th', 'Args'))];
+    for (const [key, value] of args) {
+      result.push(m('tr', m('th', key), m('td', value)));
+    }
+    return result;
+  }
+
+  getDescription(description?: Map<string, string>): m.Vnode[] {
+    if (!description) return [];
+    const result = [];
+    for (const [key, value] of description) {
+      result.push(m('tr', m('th', key), m('td', value)));
+    }
+    return result;
+  }
 }
diff --git a/ui/src/frontend/colorizer.ts b/ui/src/frontend/colorizer.ts
index f4c5780..a62f8eb 100644
--- a/ui/src/frontend/colorizer.ts
+++ b/ui/src/frontend/colorizer.ts
@@ -12,8 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {ThreadDesc} from './globals';
 import {hsl} from 'color-convert';
+import {translateState} from '../common/thread_state';
+import {ThreadDesc} from './globals';
 
 export interface Color {
   c: string;
@@ -81,19 +82,31 @@
   s: 0,
   l: 87
 };
+const ORANGE: Color = {
+  c: 'orange',
+  h: 36,
+  s: 100,
+  l: 50
+};
+const INDIGO: Color = {
+  c: 'indigo',
+  h: 231,
+  s: 48,
+  l: 48
+};
 
-export function colorForState(state: string): Readonly<Color> {
-  switch (state) {
-    case 'Running':
-    case 'Busy':
-      return DARK_GREEN;
-    case 'Runnable':
-    case 'R':
-    case 'R+':
-      return LIME_GREEN;
-    default:
-      return LIGHT_GREY;
+export function colorForState(stateCode: string): Readonly<Color> {
+  const state = translateState(stateCode);
+  if (state === 'Running') {
+    return DARK_GREEN;
+  } else if (state.startsWith('Runnable')) {
+    return LIME_GREEN;
+  } else if (state.includes('Uninterruptible Sleep')) {
+    return ORANGE;
+  } else if (state.includes('Sleeping')) {
+    return LIGHT_GREY;
   }
+  return INDIGO;
 }
 
 export function colorForTid(tid: number): Color {
diff --git a/ui/src/frontend/details_panel.ts b/ui/src/frontend/details_panel.ts
index 0e47ffe..9a3fd26 100644
--- a/ui/src/frontend/details_panel.ts
+++ b/ui/src/frontend/details_panel.ts
@@ -51,11 +51,9 @@
 interface DragHandleAttrs {
   height: number;
   resize: (height: number) => void;
-  tabs: Tab[];
+  tabs: string[];
 }
 
-export type Tab = 'current_selection'|'cpu_slices'|'android_logs';
-
 class DragHandle implements m.ClassComponent<DragHandleAttrs> {
   private dragStartHeight = 0;
   private height = 0;
@@ -65,10 +63,9 @@
   private isFullscreen = false;
   // We can't get real fullscreen height until the pan_and_zoom_handler exists.
   private fullscreenHeight = DEFAULT_DETAILS_HEIGHT_PX;
-  private tabNames = new Map<Tab, string>([
+  private tabNames = new Map<string, string>([
     ['current_selection', 'Current Selection'],
-    ['cpu_slices', 'CPU Slices'],
-    ['android_logs', 'Android Logs']
+    ['android_logs', 'Android Logs'],
   ]);
 
 
@@ -109,11 +106,14 @@
   view({attrs}: m.CVnode<DragHandleAttrs>) {
     const icon = this.isClosed ? UP_ICON : DOWN_ICON;
     const title = this.isClosed ? 'Show panel' : 'Hide panel';
-    const renderTab = (key: Tab) => {
+    const renderTab = (key: string) => {
       if (globals.frontendLocalState.currentTab === key ||
           globals.frontendLocalState.currentTab === undefined &&
               attrs.tabs[0] === key) {
-        return m('.tab[active]', this.tabNames.get(key));
+        return m(
+            '.tab[active]',
+            this.tabNames.get(key) === undefined ? key :
+                                                   this.tabNames.get(key));
       }
       return m(
           '.tab',
@@ -123,7 +123,7 @@
               globals.rafScheduler.scheduleFullRedraw();
             }
           },
-          this.tabNames.get(key));
+          this.tabNames.get(key) === undefined ? key : this.tabNames.get(key));
     };
     return m(
         '.handle',
@@ -167,7 +167,7 @@
   private showDetailsPanel = true;
 
   view() {
-    const detailsPanels: Map<Tab, AnyAttrsVnode> = new Map();
+    const detailsPanels: Map<string, AnyAttrsVnode> = new Map();
     const curSelection = globals.state.currentSelection;
     if (curSelection) {
       switch (curSelection.kind) {
@@ -213,8 +213,11 @@
       detailsPanels.set('android_logs', m(LogPanel, {}));
     }
 
-    if (globals.frontendLocalState.selectedArea.area !== undefined) {
-      detailsPanels.set('cpu_slices', m(AggregationPanel));
+    for (const [key, value] of globals.aggregateDataStore.entries()) {
+      if (value.columns.length > 0 && value.columns[0].data.length > 0) {
+        detailsPanels.set(
+            value.tabName, m(AggregationPanel, {kind: key, data: value}));
+      }
     }
 
     const wasShowing = this.showDetailsPanel;
@@ -224,7 +227,8 @@
       this.detailsHeight = DEFAULT_DETAILS_HEIGHT_PX;
     }
 
-    const panel = globals.frontendLocalState.currentTab ?
+    const panel = globals.frontendLocalState.currentTab &&
+            detailsPanels.has(globals.frontendLocalState.currentTab) ?
         detailsPanels.get(globals.frontendLocalState.currentTab) :
         detailsPanels.values().next().value;
     const panels = panel ? [panel] : [];
diff --git a/ui/src/frontend/drag_gesture_handler.ts b/ui/src/frontend/drag_gesture_handler.ts
index 8171b18..5570197 100644
--- a/ui/src/frontend/drag_gesture_handler.ts
+++ b/ui/src/frontend/drag_gesture_handler.ts
@@ -17,6 +17,7 @@
   private readonly boundOnMouseMove = this.onMouseMove.bind(this);
   private readonly boundOnMouseUp = this.onMouseUp.bind(this);
   private clientRect?: DOMRect;
+  private pendingMouseDownEvent?: MouseEvent;
 
   constructor(
       private element: HTMLElement,
@@ -29,27 +30,43 @@
   private onMouseDown(e: MouseEvent) {
     document.body.addEventListener('mousemove', this.boundOnMouseMove);
     document.body.addEventListener('mouseup', this.boundOnMouseUp);
+    this.pendingMouseDownEvent = e;
+    // Prevent interactions with other DragGestureHandlers and event listeners
+    e.stopPropagation();
+  }
+
+  // We don't start the drag gesture on mouse down, instead we wait until
+  // the mouse has moved at least 1px. This prevents accidental drags that
+  // were meant to be clicks.
+  private startDragGesture(e: MouseEvent) {
     this.clientRect = this.element.getBoundingClientRect() as DOMRect;
     this.onDragStarted(
         e.clientX - this.clientRect.left, e.clientY - this.clientRect.top);
-
-    // Prevent interactions with other DragGestureHandlers and event listeners
-    e.stopPropagation();
   }
 
   private onMouseMove(e: MouseEvent) {
     if (e.buttons === 0) {
       return this.onMouseUp(e);
     }
-    this.onDrag(
-        e.clientX - this.clientRect!.left, e.clientY - this.clientRect!.top);
+    if (this.pendingMouseDownEvent &&
+        (Math.abs(e.clientX - this.pendingMouseDownEvent.clientX) > 1 ||
+         Math.abs(e.clientY - this.pendingMouseDownEvent.clientY) > 1)) {
+      this.startDragGesture(this.pendingMouseDownEvent);
+      this.pendingMouseDownEvent = undefined;
+    }
+    if (!this.pendingMouseDownEvent) {
+      this.onDrag(
+          e.clientX - this.clientRect!.left, e.clientY - this.clientRect!.top);
+    }
     e.stopPropagation();
   }
 
   private onMouseUp(e: MouseEvent) {
     document.body.removeEventListener('mousemove', this.boundOnMouseMove);
     document.body.removeEventListener('mouseup', this.boundOnMouseUp);
-    this.onDragFinished();
+    if (!this.pendingMouseDownEvent) {
+      this.onDragFinished();
+    }
     e.stopPropagation();
   }
 }
diff --git a/ui/src/frontend/flamegraph.ts b/ui/src/frontend/flamegraph.ts
index 4c4d3e4..ddd8c39 100644
--- a/ui/src/frontend/flamegraph.ts
+++ b/ui/src/frontend/flamegraph.ts
@@ -44,8 +44,14 @@
   return totalSize;
 }
 
+export interface NodeRendering {
+  totalSize?: string;
+  selfSize?: string;
+}
+
 export class Flamegraph {
   private isThumbnail = false;
+  private nodeRendering: NodeRendering = {};
   private flamegraphData: CallsiteInfo[];
   private maxDepth = -1;
   private totalSize = -1;
@@ -98,7 +104,9 @@
    * graph.
    */
   updateDataIfChanged(
-      flamegraphData: CallsiteInfo[], clickedCallsite?: CallsiteInfo) {
+      nodeRendering: NodeRendering, flamegraphData: CallsiteInfo[],
+      clickedCallsite?: CallsiteInfo) {
+    this.nodeRendering = nodeRendering;
     this.clickedCallsite = clickedCallsite;
     if (this.flamegraphData === flamegraphData) {
       return;
@@ -237,45 +245,45 @@
       const nameText = this.getCallsiteName(this.hoveredCallsite);
       lineSplitter =
           splitIfTooBig(nameText, width, ctx.measureText(nameText).width);
-      const nameTextWidth = lineSplitter.lineWidth;
+      let textWidth = lineSplitter.lineWidth;
       lines.push(...lineSplitter.lines);
 
       const mappingText = this.hoveredCallsite.mapping;
       lineSplitter =
           splitIfTooBig(mappingText, width, ctx.measureText(mappingText).width);
-      const mappingTextWidth = lineSplitter.lineWidth;
+      textWidth = Math.max(textWidth, lineSplitter.lineWidth);
       lines.push(...lineSplitter.lines);
 
-      const percentage = this.hoveredCallsite.totalSize / this.totalSize * 100;
-      const totalSizeText = `total: ${
-          this.displaySize(
-              this.hoveredCallsite.totalSize,
-              unit,
-              unit === 'B' ? 1024 : 1000)} (${percentage.toFixed(2)}%)`;
-      lineSplitter = splitIfTooBig(
-          totalSizeText, width, ctx.measureText(totalSizeText).width);
-      const totalSizeTextWidth = lineSplitter.lineWidth;
-      lines.push(...lineSplitter.lines);
-
-      let selfSizeWidth = 0;
-      if (this.hoveredCallsite.selfSize > 0) {
-        const selfSizeText = `self: ${
+      if (this.nodeRendering.totalSize !== undefined) {
+        const percentage =
+            this.hoveredCallsite.totalSize / this.totalSize * 100;
+        const totalSizeText = `${this.nodeRendering.totalSize}: ${
             this.displaySize(
-                this.hoveredCallsite.selfSize,
+                this.hoveredCallsite.totalSize,
                 unit,
                 unit === 'B' ? 1024 : 1000)} (${percentage.toFixed(2)}%)`;
         lineSplitter = splitIfTooBig(
-            selfSizeText, width, ctx.measureText(selfSizeText).width);
-        selfSizeWidth = lineSplitter.lineWidth;
+            totalSizeText, width, ctx.measureText(totalSizeText).width);
+        textWidth = Math.max(textWidth, lineSplitter.lineWidth);
         lines.push(...lineSplitter.lines);
       }
 
-      const rectWidth = Math.max(
-                            nameTextWidth,
-                            mappingTextWidth,
-                            totalSizeTextWidth,
-                            selfSizeWidth) +
-          16;
+      if (this.nodeRendering.selfSize !== undefined &&
+          this.hoveredCallsite.selfSize > 0) {
+        const selfPercentage =
+            this.hoveredCallsite.selfSize / this.totalSize * 100;
+        const selfSizeText = `${this.nodeRendering.selfSize}: ${
+            this.displaySize(
+                this.hoveredCallsite.selfSize,
+                unit,
+                unit === 'B' ? 1024 : 1000)} (${selfPercentage.toFixed(2)}%)`;
+        lineSplitter = splitIfTooBig(
+            selfSizeText, width, ctx.measureText(selfSizeText).width);
+        textWidth = Math.max(textWidth, lineSplitter.lineWidth);
+        lines.push(...lineSplitter.lines);
+      }
+
+      const rectWidth = textWidth + 16;
       const rectXStart = this.hoveredX + 8 + rectWidth > width ?
           width - rectWidth - 8 :
           this.hoveredX + 8;
@@ -305,15 +313,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}) {
@@ -338,6 +349,12 @@
       return undefined;
     }
     const clickedCallsite = this.findSelectedCallsite(x, y);
+    // TODO(b/148596659): Allow to expand [merged] callsites. Currently,
+    // this expands to the biggest of the nodes that were merged, which
+    // is confusing, so we disallow clicking on them.
+    if (clickedCallsite === undefined || clickedCallsite.merged) {
+      return undefined;
+    }
     return clickedCallsite;
   }
 
diff --git a/ui/src/frontend/frontend_local_state.ts b/ui/src/frontend/frontend_local_state.ts
index 97acfd3..750e32c 100644
--- a/ui/src/frontend/frontend_local_state.ts
+++ b/ui/src/frontend/frontend_local_state.ts
@@ -23,8 +23,9 @@
 } from '../common/state';
 import {TimeSpan} from '../common/time';
 
-import {Tab} from './details_panel';
+import {randomColor} from './colorizer';
 import {globals} from './globals';
+import {debounce, ratelimit} from './rate_limiters';
 import {TimeScale} from './time_scale';
 
 interface Range {
@@ -39,36 +40,6 @@
   return current;
 }
 
-// Returns a wrapper around |f| which calls f at most once every |ms|ms.
-function ratelimit(f: Function, ms: number): Function {
-  let inProgess = false;
-  return () => {
-    if (inProgess) {
-      return;
-    }
-    inProgess = true;
-    window.setTimeout(() => {
-      f();
-      inProgess = false;
-    }, ms);
-  };
-}
-
-// Returns a wrapper around |f| which waits for a |ms|ms pause in calls
-// before calling |f|.
-function debounce(f: Function, ms: number): Function {
-  let timerId: undefined|number;
-  return () => {
-    if (timerId) {
-      window.clearTimeout(timerId);
-    }
-    timerId = window.setTimeout(() => {
-      f();
-      timerId = undefined;
-    }, ms);
-  };
-}
-
 // Calculate the space a scrollbar takes up so that we can subtract it from
 // the canvas width.
 function calculateScrollbarWidth() {
@@ -93,22 +64,25 @@
   perfDebug = false;
   hoveredUtid = -1;
   hoveredPid = -1;
-  hoveredTimestamp = -1;
+  hoveredLogsTimestamp = -1;
+  hoveredNoteTimestamp = -1;
   vidTimestamp = -1;
-  showTimeSelectPreview = false;
-  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>();
   prevVisibleTracks = new Set<string>();
   searchIndex = -1;
-  currentTab?: Tab;
+  currentTab?: string;
   scrollToTrackId?: string|number;
   httpRpcState: HttpRpcState = {connected: false};
   newVersionAvailable = false;
+
+  // This is used to calculate the tracks within a Y range for area selection.
+  areaY: Range = {};
+  // True if the user is in the process of doing an area selection.
+  selectingArea = false;
+
   private scrollBarWidth?: number;
 
   private _omniboxState: OmniboxState = {
@@ -151,9 +125,15 @@
   }
 
   // Sets the timestamp at which a vertical line will be drawn.
-  setHoveredTimestamp(ts: number) {
-    if (this.hoveredTimestamp === ts) return;
-    this.hoveredTimestamp = ts;
+  setHoveredLogsTimestamp(ts: number) {
+    if (this.hoveredLogsTimestamp === ts) return;
+    this.hoveredLogsTimestamp = ts;
+    globals.rafScheduler.scheduleRedraw();
+  }
+
+  setHoveredNoteTimestamp(ts: number) {
+    if (this.hoveredNoteTimestamp === ts) return;
+    this.hoveredNoteTimestamp = ts;
     globals.rafScheduler.scheduleRedraw();
   }
 
@@ -163,16 +143,6 @@
     globals.rafScheduler.scheduleRedraw();
   }
 
-  setShowNotePreview(show: boolean) {
-    this.showNotePreview = show;
-    globals.rafScheduler.scheduleRedraw();
-  }
-
-  setShowTimeSelectPreview(show: boolean) {
-    this.showTimeSelectPreview = show;
-    globals.rafScheduler.scheduleRedraw();
-  }
-
   addVisibleTrack(trackId: string) {
     this.visibleTracks.add(trackId);
   }
@@ -222,21 +192,76 @@
     globals.dispatch(Actions.selectArea(this._selectedArea));
   }, 20);
 
-
   selectArea(
       startSec: number, endSec: number,
       tracks = this._selectedArea.area ? this._selectedArea.area.tracks : []) {
+    if (this.currentNoteSelectionEqualToCurrentAreaSelection()) {
+      globals.dispatch(Actions.deselect({}));
+    }
     this.showPanningHint = true;
     this._selectedArea = {
       area: {startSec, endSec, tracks},
       lastUpdate: Date.now() / 1000
     };
     this.selectAreaDebounced();
-    globals.frontendLocalState.currentTab = 'cpu_slices';
+    globals.rafScheduler.scheduleFullRedraw();
+  }
+
+  toggleTrackSelection(id: string, isTrackGroup = false) {
+    const area = this._selectedArea.area;
+    if (!area) return;
+    const index = area.tracks.indexOf(id);
+    if (index > -1) {
+      area.tracks.splice(index, 1);
+      if (isTrackGroup) {  // Also remove all child tracks.
+        for (const childTrack of globals.state.trackGroups[id].tracks) {
+          const childIndex = area.tracks.indexOf(childTrack);
+          if (childIndex > -1) {
+            area.tracks.splice(childIndex, 1);
+          }
+        }
+      }
+    } else {
+      area.tracks.push(id);
+      if (isTrackGroup) {  // Also add all child tracks.
+        for (const childTrack of globals.state.trackGroups[id].tracks) {
+          if (!area.tracks.includes(childTrack)) {
+            area.tracks.push(childTrack);
+          }
+        }
+      }
+    }
+    this._selectedArea.lastUpdate = Date.now() / 1000;
+    this.selectAreaDebounced();
+    globals.rafScheduler.scheduleFullRedraw();
+  }
+
+  toggleLockArea() {
+    if (!this._selectedArea.area) return;
+    if (this.currentNoteSelectionEqualToCurrentAreaSelection()) {
+      if (globals.state.currentSelection != null &&
+          globals.state.currentSelection.kind === 'NOTE') {
+        globals.dispatch(
+            Actions.removeNote({id: globals.state.currentSelection.id}));
+      }
+    } else {
+      const color = randomColor();
+      globals.dispatch(Actions.addAreaNote({
+        timestamp: this._selectedArea.area.startSec,
+        area: this._selectedArea.area,
+        color
+      }));
+    }
+
     globals.rafScheduler.scheduleFullRedraw();
   }
 
   deselectArea() {
+    // When an area is deselected (and it is marked) also deselect the current
+    // marked selection if it is for the same area.
+    if (this.currentNoteSelectionEqualToCurrentAreaSelection()) {
+      globals.dispatch(Actions.deselect({}));
+    }
     this._selectedArea = {lastUpdate: Date.now() / 1000};
     this.selectAreaDebounced();
     globals.frontendLocalState.currentTab = undefined;
@@ -271,6 +296,27 @@
     const endSec = Math.min(ts.end, globals.state.traceTime.endSec);
     this.visibleWindowTime = new TimeSpan(startSec, endSec);
     this.timeScale.setTimeBounds(this.visibleWindowTime);
+    this.updateResolution(this.timeScale.startPx, this.timeScale.endPx);
+  }
+
+  // We lock an area selection by adding an area note. When we select the note
+  // it will also select the area but then the user can select other things,
+  // like a slice or different note and the area note will be deselected even
+  // though the area selection remains. So it is useful to know if we currently
+  // have the same area note selected and area selection.
+  private currentNoteSelectionEqualToCurrentAreaSelection() {
+    if (!this._selectedArea.area) return false;
+    if (globals.state.currentSelection != null &&
+        globals.state.currentSelection.kind === 'NOTE') {
+      const curNote = globals.state.notes[globals.state.currentSelection.id];
+      // TODO(taylori): Do the tracks need to be the same too?
+      if (curNote.noteType === 'AREA' &&
+          curNote.area.startSec === this._selectedArea.area.startSec &&
+          curNote.area.endSec === this._selectedArea.area.endSec) {
+        return true;
+      }
+    }
+    return false;
   }
 
   updateVisibleTime(ts: TimeSpan) {
@@ -284,6 +330,7 @@
 
   updateResolution(pxStart: number, pxEnd: number) {
     this.timeScale.setLimitsPx(pxStart, pxEnd);
+    this._visibleState.lastUpdate = Date.now() / 1000;
     this._visibleState.resolution = globals.getCurResolution();
     this.ratelimitedUpdateVisible();
   }
diff --git a/ui/src/frontend/globals.ts b/ui/src/frontend/globals.ts
index 7c384ba..5f0586f 100644
--- a/ui/src/frontend/globals.ts
+++ b/ui/src/frontend/globals.ts
@@ -14,7 +14,7 @@
 
 import {assertExists} from '../base/logging';
 import {DeferredAction} from '../common/actions';
-import {AggregateCpuData} from '../common/aggregation_data';
+import {AggregateData} from '../common/aggregation_data';
 import {CurrentSearchResults, SearchSummary} from '../common/search_data';
 import {CallsiteInfo, createEmptyState, State} from '../common/state';
 
@@ -25,6 +25,9 @@
 type Dispatch = (action: DeferredAction) => void;
 type TrackDataStore = Map<string, {}>;
 type QueryResultsStore = Map<string, {}>;
+type AggregateDataStore = Map<string, AggregateData>;
+type Description = Map<string, string>;
+type Args = Map<string, string>;
 export interface SliceDetails {
   ts?: number;
   dur?: number;
@@ -38,6 +41,8 @@
   wakerCpu?: number;
   category?: string;
   name?: string;
+  args?: Args;
+  description?: Description;
 }
 
 export interface CounterDetails {
@@ -48,11 +53,10 @@
 }
 
 export interface HeapProfileDetails {
+  type?: string;
   id?: number;
   ts?: number;
   tsNs?: number;
-  allocated?: number;
-  allocatedNotFreed?: number;
   pid?: number;
   upid?: number;
   flamegraph?: CallsiteInfo[];
@@ -92,6 +96,7 @@
   private _trackDataStore?: TrackDataStore = undefined;
   private _queryResults?: QueryResultsStore = undefined;
   private _overviewStore?: OverviewStore = undefined;
+  private _aggregateDataStore?: AggregateDataStore = undefined;
   private _threadMap?: ThreadMap = undefined;
   private _sliceDetails?: SliceDetails = undefined;
   private _counterDetails?: CounterDetails = undefined;
@@ -99,15 +104,7 @@
   private _numQueriesQueued = 0;
   private _bufferUsage?: number = undefined;
   private _recordingLog?: string = undefined;
-  private _aggregateCpuData: AggregateCpuData = {
-    strings: [],
-    procNameId: new Uint16Array(0),
-    pid: new Uint32Array(0),
-    threadNameId: new Uint16Array(0),
-    tid: new Uint32Array(0),
-    totalDur: new Float64Array(0),
-    occurrences: new Uint16Array(0)
-  };
+
   private _currentSearchResults: CurrentSearchResults = {
     sliceIds: new Float64Array(0),
     tsStarts: new Float64Array(0),
@@ -122,6 +119,11 @@
     count: new Uint8Array(0),
   };
 
+  // This variable is set by the is_internal_user.js script if the user is a
+  // googler. This is used to avoid exposing features that are not ready yet
+  // for public consumption. The gated features themselves are not secret.
+  isInternalUser = false;
+
   initialize(dispatch: Dispatch, controllerWorker: Worker) {
     this._dispatch = dispatch;
     this._controllerWorker = controllerWorker;
@@ -134,6 +136,7 @@
     this._trackDataStore = new Map<string, {}>();
     this._queryResults = new Map<string, {}>();
     this._overviewStore = new Map<string, QuantizedLoad[]>();
+    this._aggregateDataStore = new Map<string, AggregateData>();
     this._threadMap = new Map<number, ThreadDesc>();
     this._sliceDetails = {};
     this._counterDetails = {};
@@ -197,12 +200,8 @@
     this._counterDetails = assertExists(click);
   }
 
-  get aggregateCpuData(): AggregateCpuData {
-    return assertExists(this._aggregateCpuData);
-  }
-
-  set aggregateCpuData(value: AggregateCpuData) {
-    this._aggregateCpuData = value;
+  get aggregateDataStore(): AggregateDataStore {
+    return assertExists(this._aggregateDataStore);
   }
 
   get heapProfileDetails() {
@@ -249,6 +248,10 @@
     this._recordingLog = recordingLog;
   }
 
+  setAggregateData(kind: string, data: AggregateData) {
+    this.aggregateDataStore.set(kind, data);
+  }
+
   getCurResolution() {
     // Truncate the resolution to the closest power of 2.
     // This effectively means the resolution changes every 6 zoom levels.
@@ -277,6 +280,7 @@
     this._overviewStore = undefined;
     this._threadMap = undefined;
     this._sliceDetails = undefined;
+    this._aggregateDataStore = undefined;
     this._numQueriesQueued = 0;
     this._currentSearchResults = {
       sliceIds: new Float64Array(0),
@@ -286,15 +290,6 @@
       sources: [],
       totalResults: 0,
     };
-    this._aggregateCpuData = {
-      strings: [],
-      procNameId: new Uint16Array(0),
-      pid: new Uint32Array(0),
-      threadNameId: new Uint16Array(0),
-      tid: new Uint32Array(0),
-      totalDur: new Float64Array(0),
-      occurrences: new Uint16Array(0)
-    };
   }
 
   // Used when switching to the legacy TraceViewer UI.
diff --git a/ui/src/frontend/gridline_helper.ts b/ui/src/frontend/gridline_helper.ts
index b61729e..a7c3c27 100644
--- a/ui/src/frontend/gridline_helper.ts
+++ b/ui/src/frontend/gridline_helper.ts
@@ -70,13 +70,14 @@
     Array<[number, number]> {
   const desiredSteps = width / DESIRED_PX_PER_STEP;
   const step = getGridStepSize(span.duration, desiredSteps);
+  const actualSteps = Math.floor(span.duration / step);
   const start = Math.round(span.start / step) * step;
   const lines: Array<[number, number]> = [];
   let previousTimestamp = Number.NEGATIVE_INFINITY;
   // Iterating over the number of steps instead of
   // for (let s = start; s < span.end; s += step) because if start is very large
   // number and step very small, s will never reach end.
-  for (let i = 0; i < desiredSteps; i++) {
+  for (let i = 0; i <= actualSteps; i++) {
     let xPos = TRACK_SHELL_WIDTH;
     const timestamp = start + i * step;
     xPos += Math.floor(timescale.timeToPx(timestamp));
diff --git a/ui/src/frontend/heap_profile_panel.ts b/ui/src/frontend/heap_profile_panel.ts
index a210a9e..e9e480a 100644
--- a/ui/src/frontend/heap_profile_panel.ts
+++ b/ui/src/frontend/heap_profile_panel.ts
@@ -17,39 +17,69 @@
 import {Actions} from '../common/actions';
 import {
   ALLOC_SPACE_MEMORY_ALLOCATED_KEY,
-  DEFAULT_VIEWING_OPTION,
   OBJECTS_ALLOCATED_KEY,
   OBJECTS_ALLOCATED_NOT_FREED_KEY,
-  SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY
+  SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY,
 } from '../common/flamegraph_util';
+import {HeapProfileFlamegraphViewingOption} from '../common/state';
 import {timeToCode} from '../common/time';
 
-import {Flamegraph} from './flamegraph';
+import {Flamegraph, NodeRendering} from './flamegraph';
 import {globals} from './globals';
 import {Panel, PanelSize} from './panel';
+import {debounce} from './rate_limiters';
 
 interface HeapProfileDetailsPanelAttrs {}
 
 const HEADER_HEIGHT = 30;
 
+enum ProfileType {
+  NATIVE_HEAP_PROFILE = 'native',
+  JAVA_HEAP_GRAPH = 'graph',
+}
+
+function isProfileType(s: string): s is ProfileType {
+  return Object.values(ProfileType).includes(s as ProfileType);
+}
+
+function toProfileType(s: string): ProfileType {
+  if (!isProfileType(s)) {
+    throw new Error('Unknown type ${s}');
+  }
+  return s;
+}
+
+const RENDER_SELF_AND_TOTAL: NodeRendering = {
+  selfSize: 'Self',
+  totalSize: 'Total',
+};
+const RENDER_OBJ_COUNT: NodeRendering = {
+  selfSize: 'Self objects',
+  totalSize: 'Subtree objects',
+};
+
 export class HeapProfileDetailsPanel extends
     Panel<HeapProfileDetailsPanelAttrs> {
+  private profileType?: ProfileType = undefined;
   private ts = 0;
   private pid = 0;
   private flamegraph: Flamegraph = new Flamegraph([]);
-  private currentViewingOption = DEFAULT_VIEWING_OPTION;
+  private focusRegex = '';
+  private updateFocusRegexDebounced = debounce(() => {
+    this.updateFocusRegex();
+  }, 20);
 
   view() {
     const heapDumpInfo = globals.heapProfileDetails;
-    if (heapDumpInfo && heapDumpInfo.ts !== undefined &&
-        heapDumpInfo.allocated !== undefined &&
-        heapDumpInfo.allocatedNotFreed !== undefined &&
-        heapDumpInfo.tsNs !== undefined && heapDumpInfo.pid !== undefined &&
-        heapDumpInfo.upid !== undefined) {
+    if (heapDumpInfo && heapDumpInfo.type !== undefined &&
+        heapDumpInfo.ts !== undefined && heapDumpInfo.tsNs !== undefined &&
+        heapDumpInfo.pid !== undefined && heapDumpInfo.upid !== undefined) {
+      this.profileType = toProfileType(heapDumpInfo.type);
       this.ts = heapDumpInfo.tsNs;
       this.pid = heapDumpInfo.pid;
       if (heapDumpInfo.flamegraph) {
-        this.flamegraph.updateDataIfChanged(heapDumpInfo.flamegraph);
+        this.flamegraph.updateDataIfChanged(
+            this.nodeRendering(), heapDumpInfo.flamegraph);
       }
       const height = heapDumpInfo.flamegraph ?
           this.flamegraph.getHeight() + HEADER_HEIGHT :
@@ -77,16 +107,26 @@
             }
           },
           m('.details-panel-heading.heap-profile',
+            {onclick: (e: MouseEvent) => e.stopPropagation()},
             [
               m('div.options',
                 [
-                  m('div.title', `Heap Profile:`),
+                  m('div.title', this.getTitle()),
                   this.getViewingOptionButtons(),
                 ]),
               m('div.details',
                 [
                   m('div.time',
                     `Snapshot time: ${timeToCode(heapDumpInfo.ts)}`),
+                  m('input[type=text][placeholder=Focus]', {
+                    oninput: (e: Event) => {
+                      const target = (e.target as HTMLInputElement);
+                      this.focusRegex = target.value;
+                      this.updateFocusRegexDebounced();
+                    },
+                    // Required to stop hot-key handling:
+                    onkeydown: (e: Event) => e.stopPropagation(),
+                  }),
                   m('button.download',
                     {
                       onclick: () => {
@@ -106,45 +146,89 @@
     }
   }
 
-  getButtonsClass(viewingOption = DEFAULT_VIEWING_OPTION): string {
-    return this.currentViewingOption === viewingOption ? '.chosen' : '';
+  private getTitle(): string {
+    switch (this.profileType!) {
+      case ProfileType.NATIVE_HEAP_PROFILE:
+        return 'Heap Profile:';
+      case ProfileType.JAVA_HEAP_GRAPH:
+        return 'Java Heap:';
+      default:
+        throw new Error('unknown type');
+    }
+  }
+
+  private nodeRendering(): NodeRendering {
+    if (this.profileType === undefined) {
+      return {};
+    }
+    const viewingOption =
+        globals.state.currentHeapProfileFlamegraph!.viewingOption;
+    switch (this.profileType) {
+      case ProfileType.NATIVE_HEAP_PROFILE:
+        return RENDER_SELF_AND_TOTAL;
+      case ProfileType.JAVA_HEAP_GRAPH:
+        if (viewingOption === OBJECTS_ALLOCATED_NOT_FREED_KEY) {
+          return RENDER_OBJ_COUNT;
+        } else {
+          return RENDER_SELF_AND_TOTAL;
+        }
+      default:
+        throw new Error('unknown type');
+    }
+  }
+
+  private updateFocusRegex() {
+    globals.dispatch(Actions.changeFocusHeapProfileFlamegraph({
+      focusRegex: this.focusRegex,
+    }));
+  }
+
+  getButtonsClass(button: HeapProfileFlamegraphViewingOption): string {
+    if (globals.state.currentHeapProfileFlamegraph === null) return '';
+    return globals.state.currentHeapProfileFlamegraph.viewingOption === button ?
+        '.chosen' :
+        '';
   }
 
   getViewingOptionButtons(): m.Children {
-    return m(
-        'div',
-        m(`button${this.getButtonsClass(SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY)}`,
-          {
-            onclick: () => {
-              this.changeViewingOption(SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY);
-            }
-          },
-          'space'),
-        m(`button${this.getButtonsClass(ALLOC_SPACE_MEMORY_ALLOCATED_KEY)}`,
-          {
-            onclick: () => {
-              this.changeViewingOption(ALLOC_SPACE_MEMORY_ALLOCATED_KEY);
-            }
-          },
-          'alloc_space'),
-        m(`button${this.getButtonsClass(OBJECTS_ALLOCATED_NOT_FREED_KEY)}`,
-          {
-            onclick: () => {
-              this.changeViewingOption(OBJECTS_ALLOCATED_NOT_FREED_KEY);
-            }
-          },
-          'objects'),
-        m(`button${this.getButtonsClass(OBJECTS_ALLOCATED_KEY)}`,
-          {
-            onclick: () => {
-              this.changeViewingOption(OBJECTS_ALLOCATED_KEY);
-            }
-          },
-          'alloc_objects'));
+    const viewingOptions = [
+      m(`button${this.getButtonsClass(SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY)}`,
+        {
+          onclick: () => {
+            this.changeViewingOption(SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY);
+          }
+        },
+        'space'),
+      m(`button${this.getButtonsClass(OBJECTS_ALLOCATED_NOT_FREED_KEY)}`,
+        {
+          onclick: () => {
+            this.changeViewingOption(OBJECTS_ALLOCATED_NOT_FREED_KEY);
+          }
+        },
+        'objects'),
+    ];
+
+    if (this.profileType === ProfileType.NATIVE_HEAP_PROFILE) {
+      viewingOptions.push(
+          m(`button${this.getButtonsClass(ALLOC_SPACE_MEMORY_ALLOCATED_KEY)}`,
+            {
+              onclick: () => {
+                this.changeViewingOption(ALLOC_SPACE_MEMORY_ALLOCATED_KEY);
+              }
+            },
+            'alloc space'),
+          m(`button${this.getButtonsClass(OBJECTS_ALLOCATED_KEY)}`,
+            {
+              onclick: () => {
+                this.changeViewingOption(OBJECTS_ALLOCATED_KEY);
+              }
+            },
+            'alloc objects'));
+    }
+    return m('div', ...viewingOptions);
   }
 
-  changeViewingOption(viewingOption: string) {
-    this.currentViewingOption = viewingOption;
+  changeViewingOption(viewingOption: HeapProfileFlamegraphViewingOption) {
     globals.dispatch(Actions.changeViewHeapProfileFlamegraph({viewingOption}));
   }
 
@@ -159,14 +243,17 @@
   private changeFlamegraphData() {
     const data = globals.heapProfileDetails;
     const flamegraphData = data.flamegraph === undefined ? [] : data.flamegraph;
-    this.flamegraph.updateDataIfChanged(flamegraphData, data.expandedCallsite);
+    this.flamegraph.updateDataIfChanged(
+        this.nodeRendering(), flamegraphData, data.expandedCallsite);
   }
 
   renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
     this.changeFlamegraphData();
+    const current = globals.state.currentHeapProfileFlamegraph;
+    if (current === null) return;
     const unit =
-        this.currentViewingOption === SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY ||
-            this.currentViewingOption === ALLOC_SPACE_MEMORY_ALLOCATED_KEY ?
+        current.viewingOption === SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY ||
+            current.viewingOption === ALLOC_SPACE_MEMORY_ALLOCATED_KEY ?
         'B' :
         '';
     this.flamegraph.draw(ctx, size.width, size.height, 0, HEADER_HEIGHT, unit);
diff --git a/ui/src/frontend/help_modal.ts b/ui/src/frontend/help_modal.ts
index 1227d47..114438d 100644
--- a/ui/src/frontend/help_modal.ts
+++ b/ui/src/frontend/help_modal.ts
@@ -64,8 +64,8 @@
               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('td', keycap('m'), ' (with event or area selected)'),
+              m('td', 'Mark the area')),
             m('tr', m('td', keycap('?')), m('td', 'Show help')),
             )),
     buttons: [],
diff --git a/ui/src/frontend/icons.ts b/ui/src/frontend/icons.ts
new file mode 100644
index 0000000..8e37246
--- /dev/null
+++ b/ui/src/frontend/icons.ts
@@ -0,0 +1,23 @@
+// 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.
+
+export const BLANK_CHECKBOX = 'check_box_outline_blank';
+export const CHECKBOX = 'check_box';
+export const INDETERMINATE_CHECKBOX = 'indeterminate_check_box';
+
+export const EXPAND_DOWN = 'expand_more';
+export const EXPAND_UP = 'expand_less';
+
+export const STAR = 'star';
+export const STAR_BORDER = 'star_border';
diff --git a/ui/src/frontend/index.ts b/ui/src/frontend/index.ts
index e1d5c64..7823f28 100644
--- a/ui/src/frontend/index.ts
+++ b/ui/src/frontend/index.ts
@@ -21,7 +21,7 @@
 import {assertExists, reportError, setErrorHandler} from '../base/logging';
 import {forwardRemoteCalls} from '../base/remote';
 import {Actions} from '../common/actions';
-import {AggregateCpuData} from '../common/aggregation_data';
+import {AggregateData} from '../common/aggregation_data';
 import {
   LogBoundsKey,
   LogEntriesKey,
@@ -175,8 +175,8 @@
     this.redraw();
   }
 
-  publishAggregateCpuData(args: AggregateCpuData) {
-    globals.aggregateCpuData = args;
+  publishAggregateData(args: {data: AggregateData, kind: string}) {
+    globals.setAggregateData(args.kind, args.data);
     this.redraw();
   }
 
diff --git a/ui/src/frontend/keyboard_event_handler.ts b/ui/src/frontend/keyboard_event_handler.ts
index 642722e..5b08351 100644
--- a/ui/src/frontend/keyboard_event_handler.ts
+++ b/ui/src/frontend/keyboard_event_handler.ts
@@ -27,7 +27,13 @@
 export function handleKey(e: KeyboardEvent, down: boolean) {
   const key = e.key.toLowerCase();
   if (down && 'm' === key) {
-    selectSliceSpan();
+    const selectedArea = globals.frontendLocalState.selectedArea.area;
+    if (!selectedArea && globals.state.currentSelection !== null) {
+      selectSliceSpan();
+    }
+    if (selectedArea) {
+      globals.frontendLocalState.toggleLockArea();
+    }
   }
   if (down && 'f' === key) {
     findCurrentSelection();
diff --git a/ui/src/frontend/legacy_trace_viewer.ts b/ui/src/frontend/legacy_trace_viewer.ts
index c55451d..4f90fb7 100644
--- a/ui/src/frontend/legacy_trace_viewer.ts
+++ b/ui/src/frontend/legacy_trace_viewer.ts
@@ -12,18 +12,53 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import * as m from 'mithril';
 import {inflate} from 'pako';
-
 import {assertTrue} from '../base/logging';
+import {showModal} from './modal';
 
-import {globals} from './globals';
+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 function isLegacyTrace(fileName: string): boolean {
-  fileName = fileName.toLowerCase();
-  return (
-      fileName.endsWith('.json') || fileName.endsWith('.json.gz') ||
+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) {
@@ -48,7 +83,6 @@
   }
 }
 
-
 export function openBufferWithLegacyTraceViewer(
     name: string, data: ArrayBuffer|string, size: number) {
   if (data instanceof ArrayBuffer) {
@@ -65,49 +99,36 @@
     }
   }
 
-  document.body.style.transition =
-      'filter 1s ease, transform 1s cubic-bezier(0.985, 0.005, 1.000, 0.225)';
-  document.body.style.filter = 'grayscale(1) blur(10px) opacity(0)';
-  document.body.style.transform = 'scale(0)';
-  const transitionPromise = new Promise(resolve => {
-    document.body.addEventListener('transitionend', (e: TransitionEvent) => {
-      if (e.propertyName === 'transform') {
-        resolve();
-      }
+  // The location.pathname mangling is to make this code work also when hosted
+  // in a non-root sub-directory, for the case of CI artifacts.
+  const urlParts = location.pathname.split('/');
+  urlParts[urlParts.length - 1] = 'assets/catapult_trace_viewer.html';
+  const catapultUrl = urlParts.join('/');
+  const newWin = window.open(catapultUrl) as Window;
+  if (newWin) {
+    // Popup succeedeed.
+    newWin.addEventListener('load', (e: Event) => {
+      const doc = (e.target as Document);
+      const ctl = doc.querySelector('x-profiling-view') as TraceViewerAPI;
+      ctl.setActiveTrace(name, data);
     });
-  });
+    return;
+  }
 
-  const loadPromise = new Promise(resolve => {
-    fetch('/assets/catapult_trace_viewer.html').then(resp => {
-      resp.text().then(content => {
-        resolve(content);
-      });
-    });
+  // Popup blocker detected.
+  showModal({
+    title: 'Open trace in the legacy Catapult Trace Viewer',
+    content: m(
+        'div',
+        m('div', 'You are seeing this interstitial because popups are blocked'),
+        m('div', 'Enable popups to skip this dialog next time.')),
+    buttons: [{
+      text: 'Open legacy UI',
+      primary: true,
+      id: 'open_legacy',
+      action: () => openBufferWithLegacyTraceViewer(name, data, size),
+    }],
   });
-
-  Promise.all([loadPromise, transitionPromise]).then(args => {
-    const fetchResult = args[0] as string;
-    replaceWindowWithTraceViewer(name, data, fetchResult);
-  });
-}
-
-// Replaces the contents of the current window with the Catapult's legacy
-// trace viewer HTML, passed in |htmlContent|.
-// This is in its own function to avoid leaking variables from the current
-// document we are about to destroy.
-function replaceWindowWithTraceViewer(
-    name: string, data: ArrayBuffer|string, htmlContent: string) {
-  globals.shutdown();
-  const newWin = window.open('', '_self') as Window;
-  newWin.document.open('text/html', 'replace');
-  newWin.document.addEventListener('readystatechange', () => {
-    const doc = newWin.document;
-    if (doc.readyState !== 'complete') return;
-    const ctl = doc.querySelector('x-profiling-view') as TraceViewerAPI;
-    ctl.setActiveTrace(name, data);
-  });
-  newWin.document.write(htmlContent);
-  newWin.document.close();
 }
 
 // TraceViewer method that we wire up to trigger the file load.
diff --git a/ui/src/frontend/logs_panel.ts b/ui/src/frontend/logs_panel.ts
index 2f4c485..f3b49e6 100644
--- a/ui/src/frontend/logs_panel.ts
+++ b/ui/src/frontend/logs_panel.ts
@@ -81,11 +81,11 @@
   }
 
   onRowOver(ts: number) {
-    globals.frontendLocalState.setHoveredTimestamp(ts);
+    globals.frontendLocalState.setHoveredLogsTimestamp(ts);
   }
 
   onRowOut() {
-    globals.frontendLocalState.setHoveredTimestamp(-1);
+    globals.frontendLocalState.setHoveredLogsTimestamp(-1);
   }
 
   private totalRows():
diff --git a/ui/src/frontend/notes_panel.ts b/ui/src/frontend/notes_panel.ts
index 9400990..26aa69a 100644
--- a/ui/src/frontend/notes_panel.ts
+++ b/ui/src/frontend/notes_panel.ts
@@ -15,6 +15,7 @@
 import * as m from 'mithril';
 
 import {Actions} from '../common/actions';
+import {AreaNote, Note} from '../common/state';
 import {timeToString} from '../common/time';
 
 import {randomColor} from './colorizer';
@@ -24,8 +25,8 @@
 import {Panel, PanelSize} from './panel';
 
 const FLAG_WIDTH = 16;
+const AREA_TRIANGLE_WIDTH = 10;
 const MOVIE_WIDTH = 16;
-const MOUSE_OFFSET = 6;
 const FLAG = `\uE153`;
 const MOVIE = '\uE8DA';
 
@@ -39,8 +40,7 @@
 
   oncreate({dom}: m.CVnodeDOM) {
     dom.addEventListener('mousemove', (e: Event) => {
-      this.hoveredX =
-        (e as MouseEvent).layerX - TRACK_SHELL_WIDTH - MOUSE_OFFSET;
+      this.hoveredX = (e as MouseEvent).layerX - TRACK_SHELL_WIDTH;
       if (globals.state.scrubbingEnabled) {
         const timescale = globals.frontendLocalState.timeScale;
         const timestamp = timescale.pxToTime(this.hoveredX);
@@ -49,13 +49,12 @@
       globals.rafScheduler.scheduleRedraw();
     }, {passive: true});
     dom.addEventListener('mouseenter', (e: Event) => {
-      this.hoveredX =
-        (e as MouseEvent).layerX - TRACK_SHELL_WIDTH - MOUSE_OFFSET;
+      this.hoveredX = (e as MouseEvent).layerX - TRACK_SHELL_WIDTH;
       globals.rafScheduler.scheduleRedraw();
     });
     dom.addEventListener('mouseout', () => {
       this.hoveredX = null;
-      globals.frontendLocalState.setShowNotePreview(false);
+      globals.frontendLocalState.setHoveredNoteTimestamp(-1);
       globals.rafScheduler.scheduleRedraw();
     }, {passive: true});
   }
@@ -88,29 +87,34 @@
 
     for (const note of Object.values(globals.state.notes)) {
       const timestamp = note.timestamp;
-      if (!timeScale.timeInBounds(timestamp)) continue;
-      const x = timeScale.timeToPx(timestamp);
-
+      if ((note.noteType !== 'AREA' && !timeScale.timeInBounds(timestamp)) ||
+          (note.noteType === 'AREA' &&
+           !timeScale.timeInBounds(note.area.endSec) &&
+           !timeScale.timeInBounds(note.area.startSec))) {
+        continue;
+      }
       const currentIsHovered =
-        this.hoveredX &&
-        x - MOUSE_OFFSET <= this.hoveredX &&
-        this.hoveredX < x - MOUSE_OFFSET + FLAG_WIDTH;
+          this.hoveredX && this.mouseOverNote(this.hoveredX, note);
+      if (currentIsHovered) aNoteIsHovered = true;
+
       const selection = globals.state.currentSelection;
       const isSelected = selection !== null && selection.kind === 'NOTE' &&
-                         selection.id === note.id;
+          selection.id === note.id;
+      const x = timeScale.timeToPx(timestamp);
       const left = Math.floor(x + TRACK_SHELL_WIDTH);
 
-      // Draw flag.
-      if (!aNoteIsHovered && currentIsHovered) {
-        aNoteIsHovered = true;
-        this.drawFlag(ctx, left, size.height, note.color, isSelected,
-          note.isMovie);
-      } else if (isSelected) {
-        this.drawFlag(ctx, left, size.height, note.color, /* fill */ true,
-          note.isMovie);
+      // Draw flag or marker.
+      if (note.noteType === 'AREA') {
+        this.drawAreaMarker(
+            ctx,
+            left,
+            Math.floor(
+                timeScale.timeToPx(note.area.endSec) + TRACK_SHELL_WIDTH),
+            note.color,
+            isSelected);
       } else {
-        this.drawFlag(ctx, left, size.height, note.color, false,
-          note.isMovie);
+        this.drawFlag(
+            ctx, left, size.height, note.color, note.noteType, isSelected);
       }
 
       if (note.text) {
@@ -119,44 +123,83 @@
         // Add a white semi-transparent background for the text.
         ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
         ctx.fillRect(
-            left + FLAG_WIDTH + 2, size.height, measured.width + 2, -12);
+            left + FLAG_WIDTH + 2, size.height + 2, measured.width + 2, -12);
         ctx.fillStyle = '#3c4b5d';
-        ctx.fillText(summary, left + FLAG_WIDTH + 3, size.height - 1);
+        ctx.fillText(summary, left + FLAG_WIDTH + 3, size.height + 1);
       }
     }
 
     // A real note is hovered so we don't need to see the preview line.
-    if (aNoteIsHovered) globals.frontendLocalState.setShowNotePreview(false);
+    // TODO(taylori): Change cursor to pointer here.
+    if (aNoteIsHovered) globals.frontendLocalState.setHoveredNoteTimestamp(-1);
 
     // View preview note flag when hovering on notes panel.
     if (!aNoteIsHovered && this.hoveredX !== null) {
       const timestamp = timeScale.pxToTime(this.hoveredX);
       if (timeScale.timeInBounds(timestamp)) {
-        globals.frontendLocalState.setHoveredTimestamp(timestamp);
-        globals.frontendLocalState.setShowNotePreview(true);
+        globals.frontendLocalState.setHoveredNoteTimestamp(timestamp);
         const x = timeScale.timeToPx(timestamp);
         const left = Math.floor(x + TRACK_SHELL_WIDTH);
-        this.drawFlag(ctx, left, size.height, '#aaa', /* fill */ true);
+        this.drawFlag(
+            ctx, left, size.height, '#aaa', 'DEFAULT', /* fill */ true);
       }
     }
   }
 
+  private drawAreaMarker(
+      ctx: CanvasRenderingContext2D, x: number, xEnd: number, color: string,
+      fill: boolean) {
+    ctx.fillStyle = color;
+    ctx.strokeStyle = color;
+    const topOffset = 10;
+    // Don't draw in the track shell section.
+    if (x >= globals.frontendLocalState.timeScale.startPx + TRACK_SHELL_WIDTH) {
+      // Draw left triangle.
+      ctx.beginPath();
+      ctx.moveTo(x, topOffset);
+      ctx.lineTo(x, topOffset + AREA_TRIANGLE_WIDTH);
+      ctx.lineTo(x + AREA_TRIANGLE_WIDTH, topOffset);
+      ctx.lineTo(x, topOffset);
+      if (fill) ctx.fill();
+      ctx.stroke();
+    }
+    // Draw right triangle.
+    ctx.beginPath();
+    ctx.moveTo(xEnd, topOffset);
+    ctx.lineTo(xEnd, topOffset + AREA_TRIANGLE_WIDTH);
+    ctx.lineTo(xEnd - AREA_TRIANGLE_WIDTH, topOffset);
+    ctx.lineTo(xEnd, topOffset);
+    if (fill) ctx.fill();
+    ctx.stroke();
+
+    // Start line after track shell section, join triangles.
+    const startDraw =
+        Math.max(
+            x,
+            globals.frontendLocalState.timeScale.startPx + TRACK_SHELL_WIDTH) -
+        1;
+    ctx.fillRect(startDraw, topOffset - 1, xEnd - startDraw + 1, 1);
+  }
+
   private drawFlag(
       ctx: CanvasRenderingContext2D, x: number, height: number, color: string,
-      fill?: boolean, isMovie = globals.state.flagPauseEnabled) {
+      noteType: 'DEFAULT'|'AREA'|'MOVIE', fill?: boolean) {
     const prevFont = ctx.font;
     const prevBaseline = ctx.textBaseline;
     ctx.textBaseline = 'alphabetic';
+    // Adjust height for icon font.
+    ctx.font = '24px Material Icons';
+    ctx.fillStyle = color;
+    ctx.strokeStyle = color;
+    // The ligatures have padding included that means the icon is not drawn
+    // exactly at the x value. This adjusts for that.
+    const iconPadding = 6;
     if (fill) {
-      ctx.font = '24px Material Icons';
-      ctx.fillStyle = color;
-      // Adjust height for icon font.
-      ctx.fillText(isMovie ? MOVIE : FLAG, x - MOUSE_OFFSET, height + 2);
+      ctx.fillText(
+          noteType === 'MOVIE' ? MOVIE : FLAG, x - iconPadding, height + 2);
     } else {
-      ctx.strokeStyle = color;
-      ctx.font = '24px Material Icons';
-      // Adjust height for icon font.
-      ctx.strokeText(isMovie ? MOVIE : FLAG, x - MOUSE_OFFSET, height + 2.5);
+      ctx.strokeText(
+          noteType === 'MOVIE' ? MOVIE : FLAG, x - iconPadding, height + 2.5);
     }
     ctx.font = prevFont;
     ctx.textBaseline = prevBaseline;
@@ -165,13 +208,14 @@
 
   private onClick(x: number, _: number, isMovie: boolean) {
     const timeScale = globals.frontendLocalState.timeScale;
-    const timestamp = timeScale.pxToTime(x - MOUSE_OFFSET);
-    const width = isMovie ? MOVIE_WIDTH : FLAG_WIDTH;
+    const timestamp = timeScale.pxToTime(x);
     for (const note of Object.values(globals.state.notes)) {
-      const noteX = timeScale.timeToPx(note.timestamp);
-      if (noteX <= x && x < noteX + width) {
-        if (note.isMovie) {
+      if (this.hoveredX && this.mouseOverNote(this.hoveredX, note)) {
+        if (note.noteType === 'MOVIE') {
           globals.frontendLocalState.setVidTimestamp(note.timestamp);
+        } else if (note.noteType === 'AREA') {
+          globals.frontendLocalState.selectArea(
+              note.area.startSec, note.area.endSec, note.area.tracks);
         }
         globals.makeSelection(Actions.selectNote({id: note.id}));
         return;
@@ -183,6 +227,19 @@
     const color = randomColor();
     globals.makeSelection(Actions.addNote({timestamp, color, isMovie}));
   }
+
+  private mouseOverNote(x: number, note: AreaNote|Note): boolean {
+    const timeScale = globals.frontendLocalState.timeScale;
+    const noteX = timeScale.timeToPx(note.timestamp);
+    if (note.noteType === 'AREA') {
+      return (noteX <= x && x < noteX + AREA_TRIANGLE_WIDTH) ||
+          (timeScale.timeToPx(note.area.endSec) > x &&
+           x > timeScale.timeToPx(note.area.endSec) - AREA_TRIANGLE_WIDTH);
+    } else {
+      const width = (note.noteType === 'MOVIE') ? MOVIE_WIDTH : FLAG_WIDTH;
+      return noteX <= x && x < noteX + width;
+    }
+  }
 }
 
 interface NotesEditorPanelAttrs {
@@ -225,10 +282,14 @@
             })),
           m('button',
             {
-              onclick: () =>
-                  globals.dispatch(Actions.removeNote({id: attrs.id})),
+              onclick: () => {
+                globals.dispatch(Actions.removeNote({id: attrs.id}));
+                globals.frontendLocalState.currentTab = undefined;
+                globals.rafScheduler.scheduleFullRedraw();
+              }
             },
-            'Remove')), );
+            'Remove')),
+    );
   }
 
   renderCanvas(_ctx: CanvasRenderingContext2D, _size: PanelSize) {}
diff --git a/ui/src/frontend/pan_and_zoom_handler.ts b/ui/src/frontend/pan_and_zoom_handler.ts
index 1f504b4..c5ee552 100644
--- a/ui/src/frontend/pan_and_zoom_handler.ts
+++ b/ui/src/frontend/pan_and_zoom_handler.ts
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 import {Animation} from './animation';
-import {TRACK_SHELL_WIDTH} from './css_constants';
 import {DragGestureHandler} from './drag_gesture_handler';
 import {globals} from './globals';
 import {handleKey} from './keyboard_event_handler';
@@ -44,7 +43,7 @@
 const WHEEL_ZOOM_SPEED = -0.02;
 
 const EDITING_RANGE_CURSOR = 'ew-resize';
-const DRAG_CURSOR = 'text';
+const DRAG_CURSOR = 'default';
 const PAN_CURSOR = 'move';
 
 enum Pan {
@@ -98,25 +97,38 @@
   private onSelection:
       (dragStartX: number, dragStartY: number, prevX: number, currentX: number,
        currentY: number, editing: boolean) => void;
+  private selectingStarted: () => void;
+  private selectingEnded: () => void;
 
-  constructor(
-      {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,
-          }) {
+  constructor({
+    element,
+    contentOffsetX,
+    onPanned,
+    onZoomed,
+    editSelection,
+    onSelection,
+    selectingStarted,
+    selectingEnded
+  }: {
+    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,
+    selectingStarted: () => void,
+    selectingEnded: () => void,
+  }) {
     this.element = element;
     this.contentOffsetX = contentOffsetX;
     this.onPanned = onPanned;
     this.onZoomed = onZoomed;
     this.editSelection = editSelection;
     this.onSelection = onSelection;
+    this.selectingStarted = selectingStarted;
+    this.selectingEnded = selectingEnded;
 
     document.body.addEventListener('keydown', this.boundOnKeyDown);
     document.body.addEventListener('keyup', this.boundOnKeyUp);
@@ -147,6 +159,7 @@
           if (edit) {
             this.element.style.cursor = EDITING_RANGE_CURSOR;
           } else if (!this.shiftDown) {
+            this.selectingStarted();
             this.element.style.cursor = DRAG_CURSOR;
           }
         },
@@ -155,6 +168,7 @@
           this.element.style.cursor = this.shiftDown ? PAN_CURSOR : DRAG_CURSOR;
           dragStartX = -1;
           dragStartY = -1;
+          this.selectingEnded();
         });
   }
 
@@ -217,11 +231,6 @@
         this.element.style.cursor = this.shiftDown ? PAN_CURSOR : DRAG_CURSOR;
       }
     }
-    if (!this.shiftDown) {
-      const pos = this.mousePositionX - TRACK_SHELL_WIDTH;
-      const ts = globals.frontendLocalState.timeScale.pxToTime(pos);
-      globals.frontendLocalState.setHoveredTimestamp(ts);
-    }
   }
 
   private onWheel(e: WheelEvent) {
@@ -280,15 +289,9 @@
     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 = DRAG_CURSOR;
-        const pos = this.mousePositionX - TRACK_SHELL_WIDTH;
-        const ts = globals.frontendLocalState.timeScale.pxToTime(pos);
-        globals.frontendLocalState.setHoveredTimestamp(ts);
-      }
+    } else if (this.mousePositionX) {
+      this.element.style.cursor = DRAG_CURSOR;
     }
   }
 }
diff --git a/ui/src/frontend/panel_container.ts b/ui/src/frontend/panel_container.ts
index 3c2e31f..3a252fb 100644
--- a/ui/src/frontend/panel_container.ts
+++ b/ui/src/frontend/panel_container.ts
@@ -45,6 +45,7 @@
 }
 
 interface PanelPosition {
+  id: string;
   height: number;
   width: number;
   x: number;
@@ -94,7 +95,8 @@
       const pos = this.panelPositions[i];
       const realPosX = pos.x - TRACK_SHELL_WIDTH;
       if (realPosX + pos.width >= minX && realPosX <= maxX &&
-          pos.y + pos.height >= minY && pos.y <= maxY) {
+          pos.y + pos.height >= minY && pos.y <= maxY &&
+          this.attrs.panels[i].attrs.selectable) {
         panels.push(this.attrs.panels[i]);
       }
     }
@@ -108,9 +110,23 @@
          this.prevAreaSelection.lastUpdate >= selection.lastUpdate) ||
         area === undefined ||
         globals.frontendLocalState.areaY.start === undefined ||
-        globals.frontendLocalState.areaY.end === undefined) {
+        globals.frontendLocalState.areaY.end === undefined ||
+        this.panelPositions.length === 0) {
       return;
     }
+    // Only get panels from the current panel container if the selection began
+    // in this container.
+    const panelContainerTop = this.panelPositions[0].y;
+    const panelContainerBottom =
+        this.panelPositions[this.panelPositions.length - 1].y +
+        this.panelPositions[this.panelPositions.length - 1].height;
+    if (globals.frontendLocalState.areaY.start + TOPBAR_HEIGHT <
+            panelContainerTop ||
+        globals.frontendLocalState.areaY.start + TOPBAR_HEIGHT >
+            panelContainerBottom) {
+      return;
+    }
+
     // The Y value is given from the top of the pan and zoom region, we want it
     // from the top of the panel container. The parent offset corrects that.
     const panels = this.getPanelsInRegion(
@@ -245,12 +261,7 @@
     }
 
     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);
   }
@@ -270,7 +281,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;
   }
@@ -288,8 +303,10 @@
     assertTrue(panels.length === this.attrs.panels.length);
     for (let i = 0; i < panels.length; i++) {
       const rect = panels[i].getBoundingClientRect() as DOMRect;
+      const id = this.attrs.panels[i].attrs.id ||
+          this.attrs.panels[i].attrs.trackGroupId;
       this.panelPositions[i] =
-          {height: rect.height, width: rect.width, x: rect.x, y: rect.y};
+          {id, height: rect.height, width: rect.width, x: rect.x, y: rect.y};
       this.totalPanelHeight += rect.height;
     }
 
@@ -307,9 +324,7 @@
     const canvasYStart =
         Math.floor(this.scrollTop - this.getCanvasOverdrawHeightPerSide());
 
-    if (this.attrs.kind === 'TRACKS') {
-      this.handleAreaSelection();
-    }
+    this.handleAreaSelection();
 
     let panelYStart = 0;
     const panels = assertExists(this.attrs).panels;
@@ -344,10 +359,70 @@
       this.ctx.restore();
       panelYStart += panelHeight;
     }
+
+    this.drawTopLayerOnCanvas();
     const redrawDur = debugNow() - redrawStart;
     this.updatePerfStats(redrawDur, panels.length, totalOnCanvas);
   }
 
+  // The panels each draw on the canvas but some details need to be drawn across
+  // the whole canvas rather than per panel.
+  private drawTopLayerOnCanvas() {
+    if (!this.ctx) return;
+    const selection = globals.frontendLocalState.selectedArea;
+    const area = selection.area;
+    if (area === undefined ||
+        globals.frontendLocalState.areaY.start === undefined ||
+        globals.frontendLocalState.areaY.end === undefined ||
+        !globals.frontendLocalState.selectingArea) {
+      return;
+    }
+    if (this.panelPositions.length === 0 || area.tracks.length === 0) return;
+
+    // Find the minY and maxY of the selected tracks in this panel container.
+    const panelContainerTop = this.panelPositions[0].y;
+    const panelContainerBottom =
+        this.panelPositions[this.panelPositions.length - 1].y +
+        this.panelPositions[this.panelPositions.length - 1].height;
+    let selectedTracksMinY = panelContainerBottom;
+    let selectedTracksMaxY = panelContainerTop;
+    let trackFromCurrentContainerSelected = false;
+    for (let i = 0; i < this.panelPositions.length; i++) {
+      if (area.tracks.includes(this.panelPositions[i].id)) {
+        trackFromCurrentContainerSelected = true;
+        selectedTracksMinY =
+            Math.min(selectedTracksMinY, this.panelPositions[i].y);
+        selectedTracksMaxY = Math.max(
+            selectedTracksMaxY,
+            this.panelPositions[i].y + this.panelPositions[i].height);
+      }
+    }
+
+    // No box should be drawn if there are no selected tracks in the current
+    // container.
+    if (!trackFromCurrentContainerSelected) {
+      return;
+    }
+
+    const startX = globals.frontendLocalState.timeScale.timeToPx(area.startSec);
+    const endX = globals.frontendLocalState.timeScale.timeToPx(area.endSec);
+    // To align with where to draw on the canvas subtract the first panel Y.
+    selectedTracksMinY -= panelContainerTop;
+    selectedTracksMaxY -= panelContainerTop;
+    this.ctx.save();
+    this.ctx.strokeStyle = 'rgba(52,69,150)';
+    this.ctx.lineWidth = 1;
+    const canvasYStart =
+        Math.floor(this.scrollTop - this.getCanvasOverdrawHeightPerSide());
+    this.ctx.translate(TRACK_SHELL_WIDTH, -canvasYStart);
+    this.ctx.strokeRect(
+        startX,
+        selectedTracksMaxY,
+        endX - startX,
+        selectedTracksMinY - selectedTracksMaxY);
+    this.ctx.restore();
+  }
+
   private updatePanelStats(
       panelIndex: number, panel: Panel, renderTime: number,
       ctx: CanvasRenderingContext2D, size: PanelSize) {
diff --git a/ui/src/frontend/post_message_handler.ts b/ui/src/frontend/post_message_handler.ts
index 1cc4285..5ec728b 100644
--- a/ui/src/frontend/post_message_handler.ts
+++ b/ui/src/frontend/post_message_handler.ts
@@ -12,14 +12,25 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import * as m from 'mithril';
+
 import {Actions} from '../common/actions';
 
 import {globals} from './globals';
+import {showModal} from './modal';
 
-const VALID_ORIGINS = [
-  'https://chrometto.googleplex.com',
-  'https://uma.googleplex.com',
-];
+// Returns whether incoming traces should be opened automatically or should
+// instead require a user interaction.
+function isTrustedOrigin(origin: string): boolean {
+  const TRUSTED_ORIGINS = [
+    'https://chrometto.googleplex.com',
+    'https://uma.googleplex.com',
+  ];
+  if (TRUSTED_ORIGINS.includes(origin)) return true;
+  if (new URL(origin).hostname.endsWith('corp.google.com')) return true;
+  return false;
+}
+
 
 // The message handler supports loading traces from an ArrayBuffer.
 // There is no other requirement than sending the ArrayBuffer as the |data|
@@ -28,12 +39,6 @@
 // ready, so the message handler always replies to a 'PING' message with 'PONG',
 // which indicates it is ready to receive a trace.
 export function postMessageHandler(messageEvent: MessageEvent) {
-  if (!isOriginAllowed(messageEvent.origin)) {
-    console.error(
-        `Ignoring message - disallowed origin ${messageEvent.origin}`);
-    return;
-  }
-
   if (document.readyState !== 'complete') {
     console.error('Ignoring message - document not ready yet.');
     return;
@@ -43,6 +48,11 @@
     throw new Error('Incoming message has no source');
   }
 
+  // This can happen if an extension tries to postMessage.
+  if (messageEvent.source !== window.opener) {
+    return;
+  }
+
   if (!('data' in messageEvent)) {
     throw new Error('Incoming message has no data property');
   }
@@ -65,18 +75,29 @@
     throw new Error('Incoming message trace buffer is empty');
   }
 
-  // For external traces, we need to disable other features such as downloading
-  // and sharing a trace.
-  globals.frontendLocalState.localOnlyMode = true;
+  const openTrace = () => {
+    // For external traces, we need to disable other features such as
+    // downloading and sharing a trace.
+    globals.frontendLocalState.localOnlyMode = true;
+    globals.dispatch(Actions.openTraceFromBuffer({buffer}));
+  };
 
-  globals.dispatch(Actions.openTraceFromBuffer({buffer}));
-}
+  // If the origin is trusted open the trace directly.
+  if (isTrustedOrigin(messageEvent.origin)) {
+    openTrace();
+    return;
+  }
 
-// Returns whether messages from the origin should be accepted.
-function isOriginAllowed(origin: string): boolean {
-  if (VALID_ORIGINS.includes(origin)) return true;
-
-  if (new URL(origin).hostname.endsWith('corp.google.com')) return true;
-
-  return false;
+  // If not ask the user if they expect this and trust the origin.
+  showModal({
+    title: 'Open trace?',
+    content:
+        m('div',
+          m('div', `${messageEvent.origin} is trying to open a trace file.`),
+          m('div', 'Do you trust the origin and want to proceed?')),
+    buttons: [
+      {text: 'NO', primary: true, id: 'pm_reject_trace', action: () => {}},
+      {text: 'YES', primary: false, id: 'pm_open_trace', action: openTrace},
+    ],
+  });
 }
diff --git a/ui/src/frontend/rate_limiters.ts b/ui/src/frontend/rate_limiters.ts
new file mode 100644
index 0000000..ae212d4
--- /dev/null
+++ b/ui/src/frontend/rate_limiters.ts
@@ -0,0 +1,43 @@
+// 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.
+
+// Returns a wrapper around |f| which calls f at most once every |ms|ms.
+export function ratelimit(f: Function, ms: number): Function {
+  let inProgess = false;
+  return () => {
+    if (inProgess) {
+      return;
+    }
+    inProgess = true;
+    window.setTimeout(() => {
+      f();
+      inProgess = false;
+    }, ms);
+  };
+}
+
+// Returns a wrapper around |f| which waits for a |ms|ms pause in calls
+// before calling |f|.
+export function debounce(f: Function, ms: number): Function {
+  let timerId: undefined|number;
+  return () => {
+    if (timerId) {
+      window.clearTimeout(timerId);
+    }
+    timerId = window.setTimeout(() => {
+      f();
+      timerId = undefined;
+    }, ms);
+  };
+}
diff --git a/ui/src/frontend/record_page.ts b/ui/src/frontend/record_page.ts
index a1d1b54..2e4498d 100644
--- a/ui/src/frontend/record_page.ts
+++ b/ui/src/frontend/record_page.ts
@@ -45,9 +45,7 @@
 } from './record_widgets';
 import {Router} from './router';
 
-
-
-const POLL_RATE_MS = [250, 500, 1000, 2500, 5000, 30000, 60000];
+const POLL_INTERVAL_MS = [250, 500, 1000, 2500, 5000, 30000, 60000];
 
 const ATRACE_CATEGORIES = new Map<string, string>();
 ATRACE_CATEGORIES.set('gfx', 'Graphics');
@@ -78,10 +76,12 @@
 ATRACE_CATEGORIES.set('rro', 'Resource Overlay');
 
 const LOG_BUFFERS = new Map<string, string>();
+LOG_BUFFERS.set('LID_DEFAULT', 'Main');
 LOG_BUFFERS.set('LID_RADIO', 'Radio');
 LOG_BUFFERS.set('LID_EVENTS', 'Binary events');
 LOG_BUFFERS.set('LID_SYSTEM', 'System');
 LOG_BUFFERS.set('LID_CRASH', 'Crash');
+LOG_BUFFERS.set('LID_STATS', 'Stats');
 LOG_BUFFERS.set('LID_SECURITY', 'Security');
 LOG_BUFFERS.set('LID_KERNEL', 'Kernel');
 
@@ -191,9 +191,9 @@
           isEnabled: (cfg) => cfg.batteryDrain
         } as ProbeAttrs,
         m(Slider, {
-          title: 'Poll rate',
+          title: 'Poll interval',
           cssClass: '.thin',
-          values: POLL_RATE_MS,
+          values: POLL_INTERVAL_MS,
           unit: 'ms',
           set: (cfg, val) => cfg.batteryDrainPollMs = val,
           get: (cfg) => cfg.batteryDrainPollMs
@@ -232,9 +232,9 @@
           isEnabled: (cfg) => cfg.cpuCoarse
         } as ProbeAttrs,
         m(Slider, {
-          title: 'Poll rate',
+          title: 'Poll interval',
           cssClass: '.thin',
-          values: POLL_RATE_MS,
+          values: POLL_INTERVAL_MS,
           unit: 'ms',
           set: (cfg, val) => cfg.cpuCoarsePollMs = val,
           get: (cfg) => cfg.cpuCoarsePollMs
@@ -304,7 +304,7 @@
   ];
 
   return m(
-      `.record-section${cssClass}`,
+      `.${cssClass}`,
       m(Textarea, {
         title: 'Names or pids of the processes to track',
         placeholder: 'One per line, e.g.:\n' +
@@ -366,6 +366,58 @@
   );
 }
 
+function JavaHeapDumpSettings(cssClass: string) {
+  const valuesForMS = [
+    0,
+    1000,
+    10 * 1000,
+    30 * 1000,
+    60 * 1000,
+    5 * 60 * 1000,
+    10 * 60 * 1000,
+    30 * 60 * 1000,
+    60 * 60 * 1000
+  ];
+
+  return m(
+      `.${cssClass}`,
+      m(Textarea, {
+        title: 'Names or pids of the processes to track',
+        placeholder: 'One per line, e.g.:\n' +
+            'com.android.vending\n' +
+            '1503',
+        set: (cfg, val) => cfg.jpProcesses = val,
+        get: (cfg) => cfg.jpProcesses
+      } as TextareaAttrs),
+      m(Slider, {
+        title: 'Continuous dumps interval ',
+        description: 'Time between following dumps (0 = disabled)',
+        cssClass: '.thin',
+        values: valuesForMS,
+        unit: 'ms',
+        min: 0,
+        set: (cfg, val) => {
+          cfg.jpContinuousDumpsInterval = val;
+        },
+        get: (cfg) => cfg.jpContinuousDumpsInterval
+      } as SliderAttrs),
+      m(Slider, {
+        title: 'Continuous dumps phase',
+        description: 'Time before first dump',
+        cssClass: `.thin${
+            globals.state.recordConfig.jpContinuousDumpsInterval === 0 ?
+                '.greyed-out' :
+                ''}`,
+        values: valuesForMS,
+        unit: 'ms',
+        min: 0,
+        disabled: globals.state.recordConfig.jpContinuousDumpsInterval === 0,
+        set: (cfg, val) => cfg.jpContinuousDumpsPhase = val,
+        get: (cfg) => cfg.jpContinuousDumpsPhase
+      } as SliderAttrs),
+  );
+}
+
 function MemorySettings(cssClass: string) {
   const meminfoOpts = new Map<string, string>();
   for (const x in MeminfoCounters) {
@@ -385,8 +437,8 @@
       `.record-section${cssClass}`,
       m(Probe,
         {
-          title: 'Heap profiling',
-          img: 'heap_profiler.png',
+          title: 'Native heap profiling',
+          img: 'rec_native_heap_profiler.png',
           descr: `Track native heap allocations & deallocations of an Android
                process. (Available on Android 10+)`,
           setEnabled: (cfg, val) => cfg.heapProfiling = val,
@@ -395,6 +447,16 @@
         HeapSettings(cssClass)),
       m(Probe,
         {
+          title: 'Java heap dumps',
+          img: 'rec_java_heap_dump.png',
+          descr: `Dump information about the Java object graph of an
+          Android app. (Available on Android 11+)`,
+          setEnabled: (cfg, val) => cfg.javaHeapDump = val,
+          isEnabled: (cfg) => cfg.javaHeapDump
+        } as ProbeAttrs,
+        JavaHeapDumpSettings(cssClass)),
+      m(Probe,
+        {
           title: 'Kernel meminfo',
           img: 'rec_meminfo.png',
           descr: 'Polling of /proc/meminfo',
@@ -402,9 +464,9 @@
           isEnabled: (cfg) => cfg.meminfo
         } as ProbeAttrs,
         m(Slider, {
-          title: 'Poll rate',
+          title: 'Poll interval',
           cssClass: '.thin',
-          values: POLL_RATE_MS,
+          values: POLL_INTERVAL_MS,
           unit: 'ms',
           set: (cfg, val) => cfg.meminfoPeriodMs = val,
           get: (cfg) => cfg.meminfoPeriodMs
@@ -445,9 +507,9 @@
           isEnabled: (cfg) => cfg.procStats
         } as ProbeAttrs,
         m(Slider, {
-          title: 'Poll rate',
+          title: 'Poll interval',
           cssClass: '.thin',
-          values: POLL_RATE_MS,
+          values: POLL_INTERVAL_MS,
           unit: 'ms',
           set: (cfg, val) => cfg.procStatsPeriodMs = val,
           get: (cfg) => cfg.procStatsPeriodMs
@@ -463,9 +525,9 @@
           isEnabled: (cfg) => cfg.vmstat
         } as ProbeAttrs,
         m(Slider, {
-          title: 'Poll rate',
+          title: 'Poll interval',
           cssClass: '.thin',
-          values: POLL_RATE_MS,
+          values: POLL_INTERVAL_MS,
           unit: 'ms',
           set: (cfg, val) => cfg.vmstatPeriodMs = val,
           get: (cfg) => cfg.vmstatPeriodMs
diff --git a/ui/src/frontend/record_widgets.ts b/ui/src/frontend/record_widgets.ts
index ebc2267..20d5901 100644
--- a/ui/src/frontend/record_widgets.ts
+++ b/ui/src/frontend/record_widgets.ts
@@ -93,6 +93,7 @@
   onTimeValueChange(attrs: SliderAttrs, hms: string) {
     try {
       const date = new Date(`1970-01-01T${hms}.000Z`);
+      if (isNaN(date.getTime())) return;
       this.onValueChange(attrs, date.getTime());
     } catch {
     }
diff --git a/ui/src/frontend/service_worker_controller.ts b/ui/src/frontend/service_worker_controller.ts
index efffa21..55bcfdc 100644
--- a/ui/src/frontend/service_worker_controller.ts
+++ b/ui/src/frontend/service_worker_controller.ts
@@ -19,6 +19,7 @@
 // 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
@@ -60,20 +61,6 @@
       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.');
     }
   }
 
diff --git a/ui/src/frontend/sidebar.ts b/ui/src/frontend/sidebar.ts
index cfeeec5..3f59347 100644
--- a/ui/src/frontend/sidebar.ts
+++ b/ui/src/frontend/sidebar.ts
@@ -19,6 +19,7 @@
 import {QueryResponse} from '../common/queries';
 import {EngineMode} from '../common/state';
 
+import {Animation} from './animation';
 import {globals} from './globals';
 import {toggleHelp} from './help_modal';
 import {
@@ -141,6 +142,7 @@
         a: dispatchCreatePermalink,
         i: 'share',
         checkDownloadDisabled: true,
+        internalUserOnly: true,
       },
       {
         t: 'Download',
@@ -215,7 +217,7 @@
     items: [
       {
         t: 'Controls',
-        a: toggleHelp,
+        a: openHelp,
         i: 'help',
       },
       {
@@ -242,6 +244,11 @@
   ],
 };
 
+function openHelp(e: Event) {
+  e.preventDefault();
+  toggleHelp();
+}
+
 function getFileElement(): HTMLInputElement {
   return document.querySelector('input[type=file]')! as HTMLInputElement;
 }
@@ -260,7 +267,8 @@
   getFileElement().click();
 }
 
-function openCurrentTraceWithOldUI() {
+function openCurrentTraceWithOldUI(e: Event) {
+  e.preventDefault();
   console.assert(isTraceLoaded());
   if (!isTraceLoaded) return;
   const engine = Object.values(globals.state.engines)[0];
@@ -311,12 +319,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 +346,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) {
@@ -606,6 +617,8 @@
 
 
 export class Sidebar implements m.ClassComponent {
+  private _redrawWhileAnimating =
+      new Animation(() => globals.rafScheduler.scheduleFullRedraw());
   view() {
     const vdomSections = [];
     for (const section of SECTIONS) {
@@ -615,14 +628,19 @@
         let attrs = {
           onclick: typeof item.a === 'function' ? item.a : null,
           href: typeof item.a === 'string' ? item.a : '#',
+          target: typeof item.a === 'string' ? '_blank' : null,
           disabled: false,
         };
+        if ((item as {internalUserOnly: boolean}).internalUserOnly === true) {
+          if (!globals.isInternalUser) continue;
+        }
         if (isDownloadAndShareDisabled() &&
             item.hasOwnProperty('checkDownloadDisabled')) {
           attrs = {
             onclick: () => alert('Can not download or share external trace.'),
             href: '#',
-            disabled: true
+            target: null,
+            disabled: true,
           };
         }
         vdomItems.push(
@@ -671,11 +689,14 @@
         'nav.sidebar',
         {
           class: globals.frontendLocalState.sidebarVisible ? 'show-sidebar' :
-                                                             'hide-sidebar'
+                                                             'hide-sidebar',
+          // 150 here matches --sidebar-timing in the css.
+          ontransitionstart: () => this._redrawWhileAnimating.start(150),
+          ontransitionend: () => this._redrawWhileAnimating.stop(),
         },
         m(
             'header',
-            'Perfetto',
+            m('img[src=assets/brand.png].brand'),
             m('button.sidebar-button',
               {
                 onclick: () => {
diff --git a/ui/src/frontend/slice_panel.ts b/ui/src/frontend/slice_panel.ts
index 5f3553b..7e7e93a 100644
--- a/ui/src/frontend/slice_panel.ts
+++ b/ui/src/frontend/slice_panel.ts
@@ -55,7 +55,7 @@
                 m('th', `Thread`),
                 m('td',
                   `${threadInfo.threadName} [${threadInfo.tid}]`,
-                  m('i.material-icons',
+                  m('i.material-icons.grey',
                     {onclick: () => this.goToThread(), title: 'Go to thread'},
                     'call_made'))),
               m('tr',
diff --git a/ui/src/frontend/thread_state_panel.ts b/ui/src/frontend/thread_state_panel.ts
index 78fb4db..d6257d4 100644
--- a/ui/src/frontend/thread_state_panel.ts
+++ b/ui/src/frontend/thread_state_panel.ts
@@ -46,11 +46,10 @@
                           attrs.ts - globals.state.traceTime.startSec)}`)),
               m('tr',
                 m('th', `Duration`),
-                m('td',
-                  `${timeToCode(attrs.dur)} `,
-                  m('a',
-                    {href: 'http://b/140256335', target: '_blank'},
-                    '(b/140256335)'))),
+                m(
+                    'td',
+                    `${timeToCode(attrs.dur)} `,
+                    )),
               m('tr',
                 m('th', `State`),
                 m('td', this.getStateContent(attrs.state, attrs.cpu))),
@@ -74,7 +73,7 @@
 
     return [
       `${translateState(state)} on CPU ${cpu}`,
-      m('i.material-icons',
+      m('i.material-icons.grey',
         {
           onclick: () => {
             if (globals.sliceDetails.id && globals.sliceDetails.ts) {
diff --git a/ui/src/frontend/time_selection_panel.ts b/ui/src/frontend/time_selection_panel.ts
index 18ed125..d5dc939 100644
--- a/ui/src/frontend/time_selection_panel.ts
+++ b/ui/src/frontend/time_selection_panel.ts
@@ -44,6 +44,11 @@
   const yMid = Math.floor(target.height / 2 + target.y);
   const xWidth = xRight - xLeft;
 
+  // Don't draw in the track shell.
+  ctx.beginPath();
+  ctx.rect(bounds.x, bounds.y, bounds.width, bounds.height);
+  ctx.clip();
+
   // Draw horizontal bar of the H.
   ctx.fillRect(xLeft, yMid, xWidth, 1);
   // Draw left vertical bar of the H.
@@ -129,8 +134,9 @@
       const start = Math.min(selectedArea.startSec, selectedArea.endSec);
       const end = Math.max(selectedArea.startSec, selectedArea.endSec);
       this.renderSpan(ctx, size, new TimeSpan(start, end));
-    } else if (globals.frontendLocalState.showTimeSelectPreview) {
-      this.renderHover(ctx, size, globals.frontendLocalState.hoveredTimestamp);
+    } else if (globals.frontendLocalState.hoveredLogsTimestamp !== -1) {
+      this.renderHover(
+          ctx, size, globals.frontendLocalState.hoveredLogsTimestamp);
     }
   }
 
@@ -161,6 +167,11 @@
   }
 
   private bounds(size: PanelSize): BBox {
-    return {x: TRACK_SHELL_WIDTH, y: 0, width: size.width, height: size.height};
+    return {
+      x: TRACK_SHELL_WIDTH,
+      y: 0,
+      width: size.width - TRACK_SHELL_WIDTH,
+      height: size.height
+    };
   }
 }
diff --git a/ui/src/frontend/track.ts b/ui/src/frontend/track.ts
index d8ae5a0..4df2730 100644
--- a/ui/src/frontend/track.ts
+++ b/ui/src/frontend/track.ts
@@ -81,4 +81,39 @@
       this.renderCanvas(ctx);
     }
   }
+
+  drawTrackHoverTooltip(
+      ctx: CanvasRenderingContext2D, xPos: number, text: string,
+      text2?: string) {
+    ctx.font = '10px Roboto Condensed';
+    const textWidth = ctx.measureText(text).width;
+    let width = textWidth;
+    let textYPos = this.getHeight() / 2;
+
+    if (text2 !== undefined) {
+      const text2Width = ctx.measureText(text2).width;
+      width = Math.max(textWidth, text2Width);
+      textYPos = this.getHeight() / 2 - 6;
+    }
+
+    // Move tooltip over if it would go off the right edge of the viewport.
+    const rectWidth = width + 16;
+    const endPx = globals.frontendLocalState.timeScale.endPx;
+    if (xPos + rectWidth > endPx) {
+      xPos -= (xPos + rectWidth - endPx);
+    }
+
+    ctx.fillStyle = 'rgba(255, 255, 255, 0.9)';
+    const rectMargin = this.getHeight() / 12;
+    ctx.fillRect(
+        xPos, rectMargin, rectWidth, this.getHeight() - rectMargin * 2);
+    ctx.fillStyle = 'hsl(200, 50%, 40%)';
+    ctx.textAlign = 'left';
+    ctx.textBaseline = 'middle';
+    ctx.fillText(text, xPos + 8, textYPos);
+
+    if (text2 !== undefined) {
+      ctx.fillText(text2, xPos + 8, this.getHeight() / 2 + 6);
+    }
+  }
 }
diff --git a/ui/src/frontend/track_group_panel.ts b/ui/src/frontend/track_group_panel.ts
index f08a0e2..67d0742 100644
--- a/ui/src/frontend/track_group_panel.ts
+++ b/ui/src/frontend/track_group_panel.ts
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {hex} from 'color-convert';
 import * as m from 'mithril';
 
 import {assertExists} from '../base/logging';
@@ -24,6 +25,13 @@
 
 import {globals} from './globals';
 import {drawGridLines} from './gridline_helper';
+import {
+  BLANK_CHECKBOX,
+  CHECKBOX,
+  EXPAND_DOWN,
+  EXPAND_UP,
+  INDETERMINATE_CHECKBOX
+} from './icons';
 import {Panel, PanelSize} from './panel';
 import {Track} from './track';
 import {TrackContent} from './track_panel';
@@ -33,9 +41,9 @@
   drawVerticalSelection,
 } from './vertical_line_helper';
 
-
 interface Attrs {
   trackGroupId: string;
+  selectable: boolean;
 }
 
 export class TrackGroupPanel extends Panel<Attrs> {
@@ -81,10 +89,18 @@
     }
 
     const selectedArea = globals.frontendLocalState.selectedArea.area;
-    const markSelectedClass =
-        selectedArea && selectedArea.tracks.includes(attrs.trackGroupId) ?
-        'selected' :
-        '';
+    const trackGroup = globals.state.trackGroups[attrs.trackGroupId];
+    let checkBox = BLANK_CHECKBOX;
+    if (selectedArea) {
+      if (selectedArea.tracks.includes(attrs.trackGroupId) &&
+          trackGroup.tracks.every(id => selectedArea.tracks.includes(id))) {
+        checkBox = CHECKBOX;
+      } else if (
+          selectedArea.tracks.includes(attrs.trackGroupId) ||
+          trackGroup.tracks.some(id => selectedArea.tracks.includes(id))) {
+        checkBox = INDETERMINATE_CHECKBOX;
+      }
+    }
 
     return m(
         `.track-group-panel[collapsed=${collapsed}]`,
@@ -97,16 +113,28 @@
               })),
                   e.stopPropagation();
             },
-            class: `${highlightClass} ${markSelectedClass}`,
+            class: `${highlightClass}`,
           },
+
+          m('.fold-button',
+            m('i.material-icons',
+              this.trackGroupState.collapsed ? EXPAND_DOWN : EXPAND_UP)),
           m('h1',
             {
               title: name,
             },
             name),
-          m('.fold-button',
-            m('i.material-icons',
-              this.trackGroupState.collapsed ? 'expand_more' : 'expand_less'))),
+          selectedArea ? m('i.material-icons.track-button',
+                           {
+                             onclick: (e: MouseEvent) => {
+                               globals.frontendLocalState.toggleTrackSelection(
+                                   attrs.trackGroupId, true /*trackGroup*/);
+                               e.stopPropagation();
+                             }
+                           },
+                           checkBox) :
+                         ''),
+
         this.summaryTrack ? m(TrackContent, {track: this.summaryTrack}) : null);
   }
 
@@ -121,6 +149,19 @@
         getComputedStyle(dom).getPropertyValue('--collapsed-background');
   }
 
+  highlightIfTrackSelected(ctx: CanvasRenderingContext2D, size: PanelSize) {
+    const localState = globals.frontendLocalState;
+    const area = localState.selectedArea.area;
+    if (area && area.tracks.includes(this.trackGroupId)) {
+      ctx.fillStyle = '#ebeef9';
+      ctx.fillRect(
+          localState.timeScale.timeToPx(area.startSec) + this.shellWidth,
+          0,
+          localState.timeScale.deltaTimeToPx(area.endSec - area.startSec),
+          size.height);
+    }
+  }
+
   renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
     const collapsed = this.trackGroupState.collapsed;
     if (!collapsed) return;
@@ -130,6 +171,8 @@
     ctx.fillStyle = this.backgroundColor;
     ctx.fillRect(0, 0, size.width, size.height);
 
+    this.highlightIfTrackSelected(ctx, size);
+
     drawGridLines(
         ctx,
         globals.frontendLocalState.timeScale,
@@ -138,6 +181,7 @@
         size.height);
 
     ctx.translate(this.shellWidth, 0);
+
     if (this.summaryTrack) {
       this.summaryTrack.render(ctx);
     }
@@ -145,22 +189,24 @@
 
     const localState = globals.frontendLocalState;
     // Draw vertical line when hovering on the notes panel.
-    if (localState.showNotePreview) {
-      drawVerticalLineAtTime(ctx,
-                            localState.timeScale,
-                            localState.hoveredTimestamp,
-                            size.height,
-                            `#aaa`);
+    if (localState.hoveredNoteTimestamp !== -1) {
+      drawVerticalLineAtTime(
+          ctx,
+          localState.timeScale,
+          localState.hoveredNoteTimestamp,
+          size.height,
+          `#aaa`);
     }
-    // Draw vertical line when shift is pressed.
-    if (localState.showTimeSelectPreview) {
-      drawVerticalLineAtTime(ctx,
-                            localState.timeScale,
-                            localState.hoveredTimestamp,
-                            size.height,
-                            `rgb(52,69,150)`);
+    if (localState.hoveredLogsTimestamp !== -1) {
+      drawVerticalLineAtTime(
+          ctx,
+          localState.timeScale,
+          localState.hoveredLogsTimestamp,
+          size.height,
+          `rgb(52,69,150)`);
     }
-    if (localState.selectedArea.area !== undefined) {
+    if (localState.selectedArea.area !== undefined &&
+        !globals.frontendLocalState.selectingArea) {
       drawVerticalSelection(
           ctx,
           localState.timeScale,
@@ -177,6 +223,14 @@
                                note.timestamp,
                                size.height,
                                note.color);
+        if (note.noteType === 'AREA') {
+          drawVerticalLineAtTime(
+              ctx,
+              localState.timeScale,
+              note.area.endSec,
+              size.height,
+              note.color);
+        }
       }
       if (globals.state.currentSelection.kind === 'SLICE' &&
           globals.sliceDetails.wakeupTs !== undefined) {
@@ -188,6 +242,28 @@
             `black`);
       }
     }
+    // All marked areas should have semi-transparent vertical lines
+    // marking the start and end.
+    for (const note of Object.values(globals.state.notes)) {
+      if (note.noteType === 'AREA') {
+        const transparentNoteColor =
+            'rgba(' + hex.rgb(note.color.substr(1)).toString() + ', 0.65)';
+        drawVerticalLineAtTime(
+            ctx,
+            localState.timeScale,
+            note.area.startSec,
+            size.height,
+            transparentNoteColor,
+            1);
+        drawVerticalLineAtTime(
+            ctx,
+            localState.timeScale,
+            note.area.endSec,
+            size.height,
+            transparentNoteColor,
+            1);
+      }
+    }
   }
 }
 
diff --git a/ui/src/frontend/track_panel.ts b/ui/src/frontend/track_panel.ts
index 098bd81..0bc4754 100644
--- a/ui/src/frontend/track_panel.ts
+++ b/ui/src/frontend/track_panel.ts
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {hex} from 'color-convert';
 import * as m from 'mithril';
 
 import {Actions} from '../common/actions';
@@ -20,6 +21,7 @@
 import {TRACK_SHELL_WIDTH} from './css_constants';
 import {globals} from './globals';
 import {drawGridLines} from './gridline_helper';
+import {BLANK_CHECKBOX, CHECKBOX, STAR, STAR_BORDER} from './icons';
 import {Panel, PanelSize} from './panel';
 import {verticalScrollToTrack} from './scroll_helper';
 import {Track} from './track';
@@ -33,6 +35,11 @@
   return globals.state.pinnedTracks.indexOf(id) !== -1;
 }
 
+function isSelected(id: string) {
+  return globals.frontendLocalState.selectedArea.area &&
+      globals.frontendLocalState.selectedArea.area.tracks.includes(id);
+}
+
 interface TrackShellAttrs {
   track: Track;
   trackState: TrackState;
@@ -63,16 +70,10 @@
 
     const dragClass = this.dragging ? `drag` : '';
     const dropClass = this.dropping ? `drop-${this.dropping}` : '';
-    const selectedArea = globals.frontendLocalState.selectedArea.area;
-    const markSelectedClass =
-        selectedArea && selectedArea.tracks.includes(attrs.trackState.id) ?
-        'selected' :
-        '';
     return m(
         `.track-shell[draggable=true]`,
         {
-          class: `${highlightClass} ${markSelectedClass} ${dragClass} ${
-              dropClass}`,
+          class: `${highlightClass} ${dragClass} ${dropClass}`,
           onmousedown: this.onmousedown.bind(this),
           ondragstart: this.ondragstart.bind(this),
           ondragend: this.ondragend.bind(this),
@@ -85,16 +86,28 @@
             title: attrs.trackState.name,
           },
           attrs.trackState.name),
-        attrs.track.getTrackShellButtons(),
-        m(TrackButton, {
-          action: () => {
-            globals.dispatch(
-                Actions.toggleTrackPinned({trackId: attrs.trackState.id}));
-          },
-          i: isPinned(attrs.trackState.id) ? 'star' : 'star_border',
-          tooltip: isPinned(attrs.trackState.id) ? 'Unpin' : 'Pin to top',
-          selected: isPinned(attrs.trackState.id),
-        }));
+        m('.track-buttons',
+          attrs.track.getTrackShellButtons(),
+          m(TrackButton, {
+            action: () => {
+              globals.dispatch(
+                  Actions.toggleTrackPinned({trackId: attrs.trackState.id}));
+            },
+            i: isPinned(attrs.trackState.id) ? STAR : STAR_BORDER,
+            tooltip: isPinned(attrs.trackState.id) ? 'Unpin' : 'Pin to top',
+            showButton: isPinned(attrs.trackState.id),
+          }),
+          globals.frontendLocalState.selectedArea.area ? m(TrackButton, {
+            action: () => {
+              globals.frontendLocalState.toggleTrackSelection(
+                  attrs.trackState.id);
+            },
+            i: isSelected(attrs.trackState.id) ? CHECKBOX : BLANK_CHECKBOX,
+            tooltip: isSelected(attrs.trackState.id) ? 'Remove track' :
+                                                       'Add track to selection',
+            showButton: true,
+          }) :
+                                                         ''));
   }
 
   onmousedown(e: MouseEvent) {
@@ -169,7 +182,7 @@
       onclick: (e: MouseEvent) => {
         // If we are selecting a time range - do not pass the click to the
         // track.
-        if (e.shiftKey) return;
+        if (globals.frontendLocalState.selectingArea) return;
         // If the click is outside of the current time range, clear it.
         const clickTime = globals.frontendLocalState.timeScale.pxToTime(
             e.layerX - TRACK_SHELL_WIDTH);
@@ -220,14 +233,14 @@
   action: () => void;
   i: string;
   tooltip: string;
-  selected: boolean;
+  showButton: boolean;
 }
 export class TrackButton implements m.ClassComponent<TrackButtonAttrs> {
   view({attrs}: m.CVnode<TrackButtonAttrs>) {
     return m(
         'i.material-icons.track-button',
         {
-          class: `${attrs.selected ? 'show' : ''}`,
+          class: `${attrs.showButton ? 'show' : ''}`,
           onclick: attrs.action,
           title: attrs.tooltip,
         },
@@ -237,6 +250,7 @@
 
 interface TrackPanelAttrs {
   id: string;
+  selectable: boolean;
 }
 
 export class TrackPanel extends Panel<TrackPanelAttrs> {
@@ -253,8 +267,25 @@
     return m(TrackComponent, {trackState: this.trackState, track: this.track});
   }
 
+  highlightIfTrackSelected(ctx: CanvasRenderingContext2D, size: PanelSize) {
+    const localState = globals.frontendLocalState;
+    const area = localState.selectedArea.area;
+    if (area && area.tracks.includes(this.trackState.id)) {
+      const timeScale = localState.timeScale;
+      ctx.fillStyle = '#ebeef9';
+      ctx.fillRect(
+          timeScale.timeToPx(area.startSec) + TRACK_SHELL_WIDTH,
+          0,
+          timeScale.deltaTimeToPx(area.endSec - area.startSec),
+          size.height);
+    }
+  }
+
   renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
     ctx.save();
+
+    this.highlightIfTrackSelected(ctx, size);
+
     drawGridLines(
         ctx,
         globals.frontendLocalState.timeScale,
@@ -270,23 +301,24 @@
 
     const localState = globals.frontendLocalState;
     // Draw vertical line when hovering on the notes panel.
-    if (localState.showNotePreview) {
+    if (localState.hoveredNoteTimestamp !== -1) {
       drawVerticalLineAtTime(
           ctx,
           localState.timeScale,
-          localState.hoveredTimestamp,
+          localState.hoveredNoteTimestamp,
           size.height,
           `#aaa`);
     }
-    // Draw vertical line when shift is pressed.
-    if (localState.showTimeSelectPreview) {
-      drawVerticalLineAtTime(ctx,
-                             localState.timeScale,
-                             localState.hoveredTimestamp,
-                             size.height,
-                             `rgb(52,69,150)`);
+    if (localState.hoveredLogsTimestamp !== -1) {
+      drawVerticalLineAtTime(
+          ctx,
+          localState.timeScale,
+          localState.hoveredLogsTimestamp,
+          size.height,
+          `rgb(52,69,150)`);
     }
-    if (localState.selectedArea.area !== undefined) {
+    if (localState.selectedArea.area !== undefined &&
+        !globals.frontendLocalState.selectingArea) {
       drawVerticalSelection(
           ctx,
           localState.timeScale,
@@ -303,7 +335,16 @@
                                note.timestamp,
                                size.height,
                                note.color);
+        if (note.noteType === 'AREA') {
+          drawVerticalLineAtTime(
+              ctx,
+              localState.timeScale,
+              note.area.endSec,
+              size.height,
+              note.color);
+        }
       }
+
       if (globals.state.currentSelection.kind === 'SLICE' &&
           globals.sliceDetails.wakeupTs !== undefined) {
         drawVerticalLineAtTime(
@@ -314,5 +355,27 @@
             `black`);
       }
     }
+    // All marked areas should have semi-transparent vertical lines
+    // marking the start and end.
+    for (const note of Object.values(globals.state.notes)) {
+      if (note.noteType === 'AREA') {
+        const transparentNoteColor =
+            'rgba(' + hex.rgb(note.color.substr(1)).toString() + ', 0.65)';
+        drawVerticalLineAtTime(
+            ctx,
+            localState.timeScale,
+            note.area.startSec,
+            size.height,
+            transparentNoteColor,
+            1);
+        drawVerticalLineAtTime(
+            ctx,
+            localState.timeScale,
+            note.area.endSec,
+            size.height,
+            transparentNoteColor,
+            1);
+      }
+    }
   }
 }
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts
index 5dd811e..27d52c9 100644
--- a/ui/src/frontend/viewer_page.ts
+++ b/ui/src/frontend/viewer_page.ts
@@ -13,10 +13,11 @@
 // limitations under the License.
 
 import * as m from 'mithril';
+import {Row} from 'src/common/protos';
 
 import {Actions} from '../common/actions';
 import {QueryResponse} from '../common/queries';
-import {TimeSpan} from '../common/time';
+import {fromNs, TimeSpan} from '../common/time';
 
 import {copyToClipboard} from './clipboard';
 import {TRACK_SHELL_WIDTH} from './css_constants';
@@ -28,6 +29,10 @@
 import {PanAndZoomHandler} from './pan_and_zoom_handler';
 import {Panel} from './panel';
 import {AnyAttrsVnode, PanelContainer} from './panel_container';
+import {
+  horizontalScrollAndZoomToRange,
+  verticalScrollToTrack
+} from './scroll_helper';
 import {TickmarkPanel} from './tickmark_panel';
 import {TimeAxisPanel} from './time_axis_panel';
 import {computeZoom} from './time_scale';
@@ -39,12 +44,83 @@
 
 const SIDEBAR_WIDTH = 256;
 
+interface QueryTableRowAttrs {
+  row: Row;
+  columns: string[];
+}
+
+class QueryTableRow implements m.ClassComponent<QueryTableRowAttrs> {
+  static columnsContainsSliceLocation(columns: string[]) {
+    const requiredColumns = ['ts', 'dur', 'track_id'];
+    for (const col of requiredColumns) {
+      if (!columns.includes(col)) return false;
+    }
+    return true;
+  }
+
+  static findUiTrackId(traceTrackId: number) {
+    for (const [uiTrackId, trackState] of Object.entries(
+             globals.state.tracks)) {
+      const config = trackState.config as {trackId: number};
+      if (config.trackId === traceTrackId) return uiTrackId;
+    }
+    return null;
+  }
+
+  static rowOnClickHandler(event: Event, row: Row) {
+    // If the click bubbles up to the pan and zoom handler that will deselect
+    // the slice.
+    event.stopPropagation();
+
+    const sliceStart = fromNs(row.ts as number);
+    // row.dur can be negative. Clamp to 1ns.
+    const sliceDur = fromNs(Math.max(row.dur as number, 1));
+    const sliceEnd = sliceStart + sliceDur;
+    const trackId = row.track_id as number;
+    const uiTrackId = this.findUiTrackId(trackId);
+    if (uiTrackId === null) return;
+    verticalScrollToTrack(uiTrackId, true);
+    horizontalScrollAndZoomToRange(sliceStart, sliceEnd);
+    const sliceId = row.slice_id as number | undefined;
+    if (sliceId !== undefined) {
+      globals.makeSelection(
+          Actions.selectChromeSlice({id: sliceId, trackId: uiTrackId}));
+    }
+  }
+
+  view(vnode: m.Vnode<QueryTableRowAttrs>) {
+    const cells = [];
+    const {row, columns} = vnode.attrs;
+    for (const col of columns) {
+      cells.push(m('td', row[col]));
+    }
+    const containsSliceLocation =
+        QueryTableRow.columnsContainsSliceLocation(columns);
+    const maybeOnClick = containsSliceLocation ?
+        (e: Event) => QueryTableRow.rowOnClickHandler(e, row) :
+        null;
+    return m(
+        'tr',
+        {onclick: maybeOnClick, 'clickable': containsSliceLocation},
+        cells);
+  }
+}
+
 class QueryTable extends Panel {
+  private previousResponse?: QueryResponse;
+
+  onbeforeupdate() {
+    const resp = globals.queryResults.get('command') as QueryResponse;
+    const res = resp !== this.previousResponse;
+    return res;
+  }
+
   view() {
     const resp = globals.queryResults.get('command') as QueryResponse;
     if (resp === undefined) {
       return m('');
     }
+    this.previousResponse = resp;
     const cols = [];
     for (const col of resp.columns) {
       cols.push(m('td', col));
@@ -53,43 +129,43 @@
 
     const rows = [];
     for (let i = 0; i < resp.rows.length; i++) {
-      const cells = [];
-      for (const col of resp.columns) {
-        cells.push(m('td', resp.rows[i][col]));
-      }
-      rows.push(m('tr', cells));
+      rows.push(m(QueryTableRow, {row: resp.rows[i], columns: resp.columns}));
     }
+
     return m(
         'div',
-        m('header.overview',
-          `Query result - ${Math.round(resp.durationMs)} ms`,
-          m('span.code', resp.query),
-          resp.error ? null :
-                       m('button.query-ctrl',
-                         {
-                           onclick: () => {
-                             const lines: string[][] = [];
-                             lines.push(resp.columns);
-                             for (const row of resp.rows) {
-                               const line = [];
-                               for (const col of resp.columns) {
-                                 line.push(row[col].toString());
-                               }
-                               lines.push(line);
-                             }
-                             copyToClipboard(
-                                 lines.map(line => line.join('\t')).join('\n'));
-                           },
-                         },
-                         'Copy as .tsv'),
-          m('button.query-ctrl',
-            {
-              onclick: () => {
-                globals.queryResults.delete('command');
-                globals.rafScheduler.scheduleFullRedraw();
-              }
-            },
-            'Close'), ),
+        m(
+            'header.overview',
+            `Query result - ${Math.round(resp.durationMs)} ms`,
+            m('span.code', resp.query),
+            resp.error ?
+                null :
+                m('button.query-ctrl',
+                  {
+                    onclick: () => {
+                      const lines: string[][] = [];
+                      lines.push(resp.columns);
+                      for (const row of resp.rows) {
+                        const line = [];
+                        for (const col of resp.columns) {
+                          line.push(row[col].toString());
+                        }
+                        lines.push(line);
+                      }
+                      copyToClipboard(
+                          lines.map(line => line.join('\t')).join('\n'));
+                    },
+                  },
+                  'Copy as .tsv'),
+            m('button.query-ctrl',
+              {
+                onclick: () => {
+                  globals.queryResults.delete('command');
+                  globals.rafScheduler.scheduleFullRedraw();
+                }
+              },
+              'Close'),
+            ),
         resp.error ?
             m('.query-error', `SQL error: ${resp.error}`) :
             m('table.query-table', m('thead', header), m('tbody', rows)));
@@ -132,7 +208,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();
@@ -198,30 +277,45 @@
         if (editing) {
           const selectedArea = frontendLocalState.selectedArea.area;
           if (selectedArea !== undefined) {
-            const newTime = scale.pxToTime(currentX - TRACK_SHELL_WIDTH);
+            let newTime = scale.pxToTime(currentX - TRACK_SHELL_WIDTH);
             // Have to check again for when one boundary crosses over the other.
             const curBoundary = onTimeRangeBoundary(prevX);
             if (curBoundary == null) return;
             const keepTime = curBoundary === 'START' ? selectedArea.endSec :
                                                        selectedArea.startSec;
+            // Don't drag selection outside of current screen.
+            if (newTime < keepTime) {
+              newTime = Math.max(newTime, scale.pxToTime(scale.startPx));
+            } else {
+              newTime = Math.min(newTime, scale.pxToTime(scale.endPx));
+            }
             frontendLocalState.selectArea(
                 Math.max(Math.min(keepTime, newTime), traceTime.startSec),
                 Math.min(Math.max(keepTime, newTime), traceTime.endSec),
             );
           }
         } else {
-          frontendLocalState.setShowTimeSelectPreview(false);
-          const dragStartTime = scale.pxToTime(dragStartX - TRACK_SHELL_WIDTH);
-          const dragEndTime = scale.pxToTime(currentX - TRACK_SHELL_WIDTH);
+          const startPx = Math.max(
+              Math.min(dragStartX, currentX) - TRACK_SHELL_WIDTH,
+              scale.startPx);
+          const endPx = Math.min(
+              Math.max(dragStartX, currentX) - TRACK_SHELL_WIDTH, scale.endPx);
           frontendLocalState.selectArea(
-              Math.max(
-                  Math.min(dragStartTime, dragEndTime), traceTime.startSec),
-              Math.min(Math.max(dragStartTime, dragEndTime), traceTime.endSec),
-          );
+              scale.pxToTime(startPx), scale.pxToTime(endPx));
           frontendLocalState.areaY.start = dragStartY;
           frontendLocalState.areaY.end = currentY;
         }
         globals.rafScheduler.scheduleRedraw();
+      },
+      selectingStarted: () => {
+        globals.frontendLocalState.selectingArea = true;
+      },
+      selectingEnded: () => {
+        globals.frontendLocalState.selectingArea = false;
+        globals.frontendLocalState.areaY.start = undefined;
+        globals.frontendLocalState.areaY.end = undefined;
+        // Full redraw to color track shell.
+        globals.rafScheduler.scheduleFullRedraw();
       }
     });
   }
@@ -232,19 +326,21 @@
   }
 
   view() {
-    const scrollingPanels: AnyAttrsVnode[] =
-        globals.state.scrollingTracks.map(id => m(TrackPanel, {key: id, id}));
+    const scrollingPanels: AnyAttrsVnode[] = globals.state.scrollingTracks.map(
+        id => m(TrackPanel, {key: id, id, selectable: true}));
 
     for (const group of Object.values(globals.state.trackGroups)) {
       scrollingPanels.push(m(TrackGroupPanel, {
         trackGroupId: group.id,
         key: `trackgroup-${group.id}`,
+        selectable: true,
       }));
       if (group.collapsed) continue;
       for (const trackId of group.tracks) {
         scrollingPanels.push(m(TrackPanel, {
           key: `track-${group.id}-${trackId}`,
           id: trackId,
+          selectable: true,
         }));
       }
     }
@@ -273,7 +369,7 @@
                   m(NotesPanel, {key: 'notes'}),
                   m(TickmarkPanel, {key: 'searchTickmarks'}),
                   ...globals.state.pinnedTracks.map(
-                      id => m(TrackPanel, {key: id, id})),
+                      id => m(TrackPanel, {key: id, id, selectable: true})),
                 ],
                 kind: 'OVERVIEW',
               })),
diff --git a/ui/src/service_worker/service_worker.ts b/ui/src/service_worker/service_worker.ts
index 4226a8a..3289352 100644
--- a/ui/src/service_worker/service_worker.ts
+++ b/ui/src/service_worker/service_worker.ts
@@ -51,9 +51,23 @@
 const CACHE_NAME = 'dist-' + UI_DIST_MAP.hex_digest.substr(0, 16);
 const LOG_TAG = `ServiceWorker[${UI_DIST_MAP.hex_digest.substr(0, 16)}]: `;
 
-async function handleHttpRequest(req: Request): Promise<Response> {
-  let fetchReason = 'N/A';
+
+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
@@ -65,28 +79,21 @@
   // 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.
-  const cacheable = req.method === 'GET' && url.origin === self.location.origin;
-  if (cacheable) {
-    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}`);
-      fetchReason = 'cache miss';
-    } catch (exc) {
-      console.error(LOG_TAG + `Fetch failed for ${req.url}`, exc);
-      fetchReason = 'fetch failed';
+  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;
     }
-  } else {
-    fetchReason = `not cacheable (${req.method}, ${req.cache}, ${url.origin})`;
+    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 + `serving ${req.url} from network: ${fetchReason}`);
+  console.debug(LOG_TAG + `falling back on network fetch() for ${req.url}`);
   return fetch(req);
 }
 
@@ -106,16 +113,14 @@
     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', integrity};
+      const reqOpts:
+          RequestInit = {cache: 'reload', mode: 'same-origin', integrity};
       urlsToCache.push(new Request(file, reqOpts));
-      if (file === 'index.html') {
-        const indexPage = location.href.split('service_worker.js')[0];
-        // Disable cachinig of '/' for cases where the UI is hosted in a
-        // subdirectory, because the ci-artifacts GCS bucket doesn't support
-        // auto indexes (it has a fallback 404 page that fails the check).
-        if (indexPage === '/') {
-          urlsToCache.push(new Request(indexPage, 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);
@@ -150,5 +155,12 @@
 });
 
 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/tracks/async_slices/common.ts b/ui/src/tracks/async_slices/common.ts
index 3b7c885..daa0802 100644
--- a/ui/src/tracks/async_slices/common.ts
+++ b/ui/src/tracks/async_slices/common.ts
@@ -11,12 +11,21 @@
 // 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.
-
-export {Data} from '../chrome_slices/common';
+import {TrackData} from '../../common/track_data';
 
 export const SLICE_TRACK_KIND = 'AsyncSliceTrack';
 
 export interface Config {
   maxDepth: number;
-  trackId: number;
+  trackIds: number[];
 }
+
+export interface Data extends TrackData {
+  // Slices are stored in a columnar fashion. All fields have the same length.
+  strings: string[];
+  sliceIds: Float64Array;
+  starts: Float64Array;
+  ends: Float64Array;
+  depths: Uint16Array;
+  titles: Uint16Array;  // Index in |strings|.
+}
\ No newline at end of file
diff --git a/ui/src/tracks/async_slices/controller.ts b/ui/src/tracks/async_slices/controller.ts
index c5e5e88..77049c8 100644
--- a/ui/src/tracks/async_slices/controller.ts
+++ b/ui/src/tracks/async_slices/controller.ts
@@ -38,13 +38,13 @@
 
       await this.query(
           `create view ${this.tableName('small')} as ` +
-          `select ts,dur,depth,name,slice_id from slice ` +
-          `where track_id = ${this.config.trackId} ` +
+          `select ts,dur,layout_depth,name,id from experimental_slice_layout ` +
+          `where filter_track_ids = "${this.config.trackIds.join(',')}" ` +
           `and dur < ${minNs} ` +
           `order by ts;`);
 
       await this.query(`create virtual table ${this.tableName('span')} using
-      span_join(${this.tableName('small')} PARTITIONED depth,
+      span_join(${this.tableName('small')} PARTITIONED layout_depth,
       ${this.tableName('window')});`);
 
       this.setup = true;
@@ -63,15 +63,15 @@
 
     await this.query(
         `create view ${this.tableName('small')} as ` +
-        `select ts,dur,depth,name, slice_id from slice ` +
-        `where track_id = ${this.config.trackId} ` +
+        `select ts,dur,layout_depth,name,id from experimental_slice_layout ` +
+        `where filter_track_ids = "${this.config.trackIds.join(',')}" ` +
         `and dur < ${minNs} ` +
         `order by ts `);
 
     await this.query(
         `create view ${this.tableName('big')} as ` +
-        `select ts,dur,depth,name, slice_id from slice ` +
-        `where track_id = ${this.config.trackId} ` +
+        `select ts,dur,layout_depth,name,id from experimental_slice_layout ` +
+        `where filter_track_ids = "${this.config.trackIds.join(',')}" ` +
         `and ts >= ${startNs} - dur ` +
         `and ts <= ${endNs} ` +
         `and dur >= ${minNs} ` +
@@ -82,11 +82,11 @@
     await this.query(`create view ${this.tableName('summary')} as select
       (quantum_ts * ${minNs} + ${startNs}) as ts,
       ${minNs} as dur,
-      depth,
+      layout_depth,
       'Busy' as name,
-      -1 as slice_id
+      -1 as id
       from ${this.tableName('span')}
-      group by depth, quantum_ts
+      group by layout_depth, quantum_ts
       order by ts;`);
 
     const query = `select * from ${this.tableName('summary')} UNION ` +
@@ -94,10 +94,6 @@
 
     const rawResult = await this.query(query);
 
-    if (rawResult.error) {
-      throw new Error(`Query error "${query}": ${rawResult.error}`);
-    }
-
     const numRows = +rawResult.numRecords;
 
     const slices: Data = {
diff --git a/ui/src/tracks/chrome_slices/common.ts b/ui/src/tracks/chrome_slices/common.ts
index 41eb3c2..0d476bc 100644
--- a/ui/src/tracks/chrome_slices/common.ts
+++ b/ui/src/tracks/chrome_slices/common.ts
@@ -18,15 +18,26 @@
 
 export interface Config {
   maxDepth: number;
+  namespace: string;
   trackId: number;
 }
 
 export interface Data extends TrackData {
-  // Slices are stored in a columnar fashion. All fields have the same length.
+  // Slices are stored in a columnar fashion.
   strings: string[];
   sliceIds: Float64Array;
   starts: Float64Array;
   ends: Float64Array;
   depths: Uint16Array;
-  titles: Uint16Array;      // Index in |strings|.
-}
+  titles: Uint16Array;  // Index into strings.
+
+  // Start offset into into summary columns or -1 if not summarised.
+  summarizedOffset: Int16Array;
+  // Number of summary data points for this slice.
+  summarizedSize: Uint16Array;
+
+  // These arrays are length S where S is number of summarized slices * the
+  // items in each slice.
+  summaryNameId: Uint16Array;
+  summaryPercent: Float64Array;
+}
\ No newline at end of file
diff --git a/ui/src/tracks/chrome_slices/controller.ts b/ui/src/tracks/chrome_slices/controller.ts
index 09abcd9..1922d09 100644
--- a/ui/src/tracks/chrome_slices/controller.ts
+++ b/ui/src/tracks/chrome_slices/controller.ts
@@ -36,12 +36,12 @@
       await this.query(
           `create virtual table ${this.tableName('window')} using window;`);
 
-      await this.query(
-          `create view ${this.tableName('small')} as ` +
-          `select ts,dur,depth,name,slice_id from slice ` +
-          `where track_id = ${this.config.trackId} ` +
-          `and dur < ${minNs} ` +
-          `order by ts;`);
+      await this.query(`create view ${this.tableName('small')} as
+        select ts, dur, depth, name, id as slice_id
+        from ${this.namespaceTable('slice')}
+        where track_id = ${this.config.trackId}
+        and dur < ${minNs}
+        order by ts;`);
 
       await this.query(`create virtual table ${this.tableName('span')} using
       span_join(${this.tableName('small')} PARTITIONED depth,
@@ -61,78 +61,123 @@
     await this.query(`drop view if exists ${this.tableName('big')}`);
     await this.query(`drop view if exists ${this.tableName('summary')}`);
 
-    await this.query(
-        `create view ${this.tableName('small')} as ` +
-        `select ts,dur,depth,name,slice_id from slice ` +
-        `where track_id = ${this.config.trackId} ` +
-        `and dur < ${minNs} ` +
-        `order by ts `);
+    await this.query(`create view ${this.tableName('small')} as
+      select ts, dur, depth, name, id as slice_id
+      from ${this.namespaceTable('slice')}
+      where track_id = ${this.config.trackId}
+      and dur < ${minNs}
+      order by ts`);
 
-    await this.query(
-        `create view ${this.tableName('big')} as ` +
-        `select ts,dur,depth,name,slice_id from slice ` +
-        `where track_id = ${this.config.trackId} ` +
-        `and ts >= ${startNs} - dur ` +
-        `and ts <= ${endNs} ` +
-        `and dur >= ${minNs} ` +
-        `order by ts `);
+    await this.query(`create view ${this.tableName('big')} as
+      select ts, dur, depth, name, id as slice_id, 1.0 as percent,
+      -1 as grouping
+      from ${this.namespaceTable('slice')}
+      where track_id = ${this.config.trackId}
+      and ts >= ${startNs} - dur
+      and ts <= ${endNs}
+      and dur >= ${minNs}
+      order by ts `);
 
-    // So that busy slices never overlap, we use the start of the bucket
-    // as the ts, even though min(ts) would technically be more accurate.
     await this.query(`create view ${this.tableName('summary')} as select
-      (quantum_ts * ${minNs} + ${startNs}) as ts,
-      ${minNs} as dur,
+      min(min(ts)) over (partition by depth, quantum_ts) as ts,
+      sum(sum(dur)) over (partition by depth, quantum_ts) as dur,
       depth,
-      'Busy' as name,
-      -1 as slice_id
+      name,
+      slice_id,
+      (sum(dur) * 1.0)/(sum(sum(dur)) over
+      (partition by depth,quantum_ts)) as percent,
+      quantum_ts as grouping
       from ${this.tableName('span')}
-      group by depth, quantum_ts
+      group by depth, quantum_ts, name
       order by ts;`);
 
-    const query = `select * from ${this.tableName('summary')} UNION ` +
-        `select * from ${this.tableName('big')} order by ts limit ${LIMIT}`;
+    // Since there are more rows than slices we will output, check the number of
+    // distinct groupings to find the number of slices.
+    const totalSlicesQuery = `select (
+      (select count(1) from ${this.tableName('big')}) +
+      (select count(1) from (select distinct grouping, depth
+      from ${this.tableName('summary')})))`;
+    const totalSlices = (await this.engine.queryOneRow(totalSlicesQuery))[0];
 
-    const rawResult = await this.query(query);
+    const totalSummarizedQuery =
+        `select count(1) from ${this.tableName('summary')}`;
+    const totalSummarized =
+        (await this.engine.queryOneRow(totalSummarizedQuery))[0];
 
-    if (rawResult.error) {
-      throw new Error(`Query error "${query}": ${rawResult.error}`);
-    }
+    const query = `select * from ${this.tableName('summary')} UNION
+      select * from ${this.tableName('big')} order by ts, percent desc limit ${
+        LIMIT}`;
+    const result = await this.query(query);
 
-    const numRows = +rawResult.numRecords;
+    const numRows = +result.numRecords;
 
-    const slices: Data = {
+    const data: Data = {
       start,
       end,
       resolution,
-      length: numRows,
+      length: totalSlices,
       strings: [],
-      sliceIds: new Float64Array(numRows),
-      starts: new Float64Array(numRows),
-      ends: new Float64Array(numRows),
-      depths: new Uint16Array(numRows),
-      titles: new Uint16Array(numRows),
+      sliceIds: new Float64Array(totalSlices),
+      starts: new Float64Array(totalSlices),
+      ends: new Float64Array(totalSlices),
+      depths: new Uint16Array(totalSlices),
+      titles: new Uint16Array(totalSlices),
+      summarizedOffset: new Int16Array(totalSlices).fill(-1),
+      summarizedSize: new Uint16Array(totalSlices),
+      summaryNameId: new Uint16Array(totalSummarized),
+      summaryPercent: new Float64Array(totalSummarized)
     };
 
     const stringIndexes = new Map<string, number>();
     function internString(str: string) {
       let idx = stringIndexes.get(str);
       if (idx !== undefined) return idx;
-      idx = slices.strings.length;
-      slices.strings.push(str);
+      idx = data.strings.length;
+      data.strings.push(str);
       stringIndexes.set(str, idx);
       return idx;
     }
 
+    let outIndex = 0;
+    let summaryIndex = 0;
+    const internedVarious = internString('Various slices');
     for (let row = 0; row < numRows; row++) {
-      const cols = rawResult.columns;
-      const startSec = fromNs(+cols[0].longValues![row]);
-      slices.starts[row] = startSec;
-      slices.ends[row] = startSec + fromNs(+cols[1].longValues![row]);
-      slices.depths[row] = +cols[2].longValues![row];
-      slices.titles[row] = internString(cols[3].stringValues![row]);
-      slices.sliceIds[row] = +cols[4].longValues![row];
+      const cols = result.columns;
+      const start = +cols[0].longValues![row];
+      const dur = +cols[1].longValues![row];
+      const depth = +cols[2].longValues![row];
+      const name = cols[3].stringValues![row];
+      const percent = +(cols[5].doubleValues![row].toFixed(2));
+      const grouping = +cols[6].longValues![row];
+
+      // If it is a summarized slice, store the slice percentage breakdown.
+      if (percent !== 1) {
+        if (data.summarizedOffset[outIndex] === -1) {
+          data.summarizedOffset[outIndex] = summaryIndex;
+        }
+        data.summarizedSize[outIndex] = data.summarizedSize[outIndex] + 1;
+        data.summaryNameId[summaryIndex] = internString(name);
+        data.summaryPercent[summaryIndex] = percent;
+        summaryIndex++;
+      }
+
+      const nextGrouping =
+          row + 1 < numRows ? +cols[6].longValues![row + 1] : -1;
+      const nextDepth = row + 1 < numRows ? +cols[2].longValues![row + 1] : -1;
+      // If the next grouping or next depth is different then we have reached
+      // the end of this slice.
+      if (grouping === -1 || grouping !== nextGrouping || depth !== nextDepth) {
+        const numSummarized = data.summarizedSize[outIndex];
+        data.starts[outIndex] = fromNs(start);
+        data.ends[outIndex] = fromNs(start + dur);
+        data.titles[outIndex] =
+            numSummarized > 0 ? internedVarious : internString(name);
+        data.depths[outIndex] = depth;
+        data.sliceIds[outIndex] = +cols[4].longValues![row];
+        outIndex++;
+      }
     }
-    return slices;
+    return data;
   }
 
 }
diff --git a/ui/src/tracks/chrome_slices/frontend.ts b/ui/src/tracks/chrome_slices/frontend.ts
index 8ceaf16..5aa3e8b 100644
--- a/ui/src/tracks/chrome_slices/frontend.ts
+++ b/ui/src/tracks/chrome_slices/frontend.ts
@@ -83,19 +83,37 @@
       const titleId = data.titles[i];
       const sliceId = data.sliceIds[i];
       const title = data.strings[titleId];
+      const summarizedOffset =
+          data.summarizedOffset ? data.summarizedOffset[i] : -1;
+
       if (tEnd <= visibleWindowTime.start || tStart >= visibleWindowTime.end) {
         continue;
       }
       const rectXStart = Math.max(timeScale.timeToPx(tStart), 0);
-      const rectXEnd = Math.min(timeScale.timeToPx(tEnd), pxEnd);
-      const rectWidth = rectXEnd - rectXStart;
+      let rectXEnd = Math.min(timeScale.timeToPx(tEnd), pxEnd);
+      let rectWidth = rectXEnd - rectXStart;
+      // All slices should be at least 1px.
+      if (rectWidth < 1) {
+        rectWidth = 1;
+        rectXEnd = rectXStart + 1;
+      }
       const rectYStart = TRACK_PADDING + depth * SLICE_HEIGHT;
-
-      const hovered = titleId === this.hoveredTitleId;
       const name = title.replace(/( )?\d+/g, '');
-      const hue = title === 'Busy' ? 88 : hash(name);
+      const hue = hash(name);
       const saturation = 50;
-      ctx.fillStyle = `hsl(${hue}, ${saturation}%, ${hovered ? 30 : 65}%)`;
+      const hovered = titleId === this.hoveredTitleId;
+      if (summarizedOffset !== -1) {
+        const summarizedSize = data.summarizedSize[i];
+        const nameHues =
+            (data.summaryNameId.slice(
+                 summarizedOffset, summarizedOffset + summarizedSize))
+                .map(id => hash(data.strings[id]));
+        const percents = data.summaryPercent.slice(
+            summarizedOffset, summarizedOffset + summarizedSize);
+        colorSummarizedSlice(nameHues, percents, rectXStart, rectXEnd, hovered);
+      } else {
+        ctx.fillStyle = `hsl(${hue}, ${saturation}%, ${hovered ? 30 : 65}%)`;
+      }
       ctx.fillRect(rectXStart, rectYStart, rectWidth, SLICE_HEIGHT);
 
       // Selected case
@@ -120,6 +138,25 @@
       ctx.fillText(displayText, rectXCenter, rectYStart + SLICE_HEIGHT / 2);
     }
     drawRectOnSelected();
+
+    // Make a gradient ordered most common to least common slices within the
+    // summarized slice.
+    function colorSummarizedSlice(
+        nameHues: Uint16Array,
+        percents: Float64Array,
+        rectStart: number,
+        rectEnd: number,
+        hovered: boolean) {
+      const gradient = ctx.createLinearGradient(
+          rectStart, SLICE_HEIGHT, rectEnd, SLICE_HEIGHT);
+      let colorStop = 0;
+      for (let i = 0; i < nameHues.length; i++) {
+        const colorString = `hsl(${nameHues[i]}, 50%, ${hovered ? 30 : 65}%)`;
+        colorStop = Math.max(0, Math.min(1, colorStop + percents[i]));
+        gradient.addColorStop(colorStop, colorString);
+      }
+      ctx.fillStyle = gradient;
+    }
   }
 
   getSliceIndex({x, y}: {x: number, y: number}): number|void {
@@ -158,7 +195,7 @@
     const data = this.data();
     if (data === undefined) return false;
     const sliceId = data.sliceIds[sliceIndex];
-    if (sliceId) {
+    if (sliceId !== undefined && sliceId !== -1) {
       globals.makeSelection(Actions.selectChromeSlice(
           {id: sliceId, trackId: this.trackState.id}));
       return true;
diff --git a/ui/src/tracks/counter/common.ts b/ui/src/tracks/counter/common.ts
index 53a603d..2eb3863 100644
--- a/ui/src/tracks/counter/common.ts
+++ b/ui/src/tracks/counter/common.ts
@@ -20,7 +20,6 @@
   isQuantized: boolean;
   maximumValue: number;
   minimumValue: number;
-
   timestamps: Float64Array;
   values: Float64Array;
   ids: Float64Array;
@@ -30,6 +29,9 @@
   name: string;
   maximumValue?: number;
   minimumValue?: number;
+  startTs?: number;
+  endTs?: number;
+  namespace: string;
   trackId: number;
   scale?: 'DEFAULT'|'RELATIVE';
 }
diff --git a/ui/src/tracks/counter/controller.ts b/ui/src/tracks/counter/controller.ts
index 0be0371..deb2d46 100644
--- a/ui/src/tracks/counter/controller.ts
+++ b/ui/src/tracks/counter/controller.ts
@@ -38,23 +38,37 @@
     const endNs = toNs(end);
 
     if (!this.setup) {
+      if (this.config.namespace === undefined) {
+        await this.query(`
+          create view ${this.tableName('counter_view')} as
+          select
+            id,
+            ts,
+            dur,
+            value
+          from experimental_counter_dur
+          where track_id = ${this.config.trackId};
+        `);
+      } else {
+        await this.query(`
+          create view ${this.tableName('counter_view')} as
+          select
+            id,
+            ts,
+            lead(ts, 1, ts) over (order by ts) - ts as dur,
+            value
+          from ${this.namespaceTable('counter')}
+          where track_id = ${this.config.trackId};
+        `);
+      }
+
       const result = await this.query(`
         select max(value), min(value)
-        from counter
-        where track_id = ${this.config.trackId}`);
+        from ${this.tableName('counter_view')}`);
       this.maximumValueSeen = +result.columns[0].doubleValues![0];
       this.minimumValueSeen = +result.columns[1].doubleValues![0];
       await this.query(
-        `create virtual table ${this.tableName('window')} using window;`);
-
-      await this.query(`
-        create view ${this.tableName('counter_view')} as
-        select
-          ts,
-          lead(ts, 1, ts) over (order by ts) - ts as dur,
-          value
-        from counter
-        where track_id = ${this.config.trackId};`);
+          `create virtual table ${this.tableName('window')} using window;`);
 
       await this.query(`create virtual table ${this.tableName('span')} using
         span_join(${this.tableName('counter_view')},
@@ -64,19 +78,13 @@
 
     const result = await this.engine.queryOneRow(`
       select count(*)
-      from (
-        select
-          ts,
-          lead(ts, 1, ts) over (order by ts) as ts_end,
-        from counter
-        where track_id = ${this.config.trackId}
-      )
-      where ts <= ${endNs} and ${startNs} <= ts_end`);
+      from ${this.tableName('counter_view')}
+      where ts <= ${endNs} and ${startNs} <= ts + dur`);
 
     // Only quantize if we have too much data to draw.
     const isQuantized = result[0] > LIMIT;
-    // |resolution| is in s/px we want # ns for 10px window:
-    const bucketSizeNs = Math.round(resolution * 10 * 1e9);
+    // |resolution| is in s/px we want # ns for 1px window:
+    const bucketSizeNs = Math.round(resolution * 1e9);
     let windowStartNs = startNs;
     if (isQuantized) {
       windowStartNs = Math.floor(windowStartNs / bucketSizeNs) * bucketSizeNs;
@@ -89,7 +97,8 @@
     quantum=${isQuantized ? bucketSizeNs : 0}`);
 
     let query = `select min(ts) as ts,
-      max(value) as value
+      max(value) as value,
+      -1 as id
       from ${this.tableName('span')}
       group by quantum_ts limit ${LIMIT};`;
 
@@ -101,28 +110,18 @@
       query = `
       select *
       from (
-        select ts, value, track_id
-        from counter
-        where
-          track_id = ${this.config.trackId} and
-          ts <= ${startNs}
+        select ts, value, id
+        from ${this.tableName('counter_view')}
+        where ts <= ${startNs}
         order by ts desc
         limit 1
       )
       union
       select *
       from (
-        select ts, value, track_id
-        from (
-          select
-            ts,
-            lead(ts, 1, ts) over (order by ts) as ts_end,
-            value,
-            track_id
-          from counter
-          where track_id = ${this.config.trackId}
-        )
-        where ts <= ${endNs} and ${startNs} <= ts_end
+        select ts, value, id
+        from ${this.tableName('counter_view')}
+        where ts <= ${endNs} and ${startNs} <= ts + dur
         limit ${LIMIT}
       );`;
     }
diff --git a/ui/src/tracks/counter/frontend.ts b/ui/src/tracks/counter/frontend.ts
index 6377414..d602f41 100644
--- a/ui/src/tracks/counter/frontend.ts
+++ b/ui/src/tracks/counter/frontend.ts
@@ -70,7 +70,7 @@
       i: 'show_chart',
       tooltip: (this.config.scale === 'RELATIVE') ? 'Use zero-based scale' :
                                                     'Use relative scale',
-      selected: this.config.scale === 'RELATIVE',
+      showButton: this.config.scale === 'RELATIVE',
     }));
     return buttons;
   }
@@ -160,7 +160,6 @@
       // TODO(hjd): Add units.
       let text = (data.isQuantized) ? 'max value: ' : 'value: ';
       text += `${this.hoveredValue.toLocaleString()}`;
-      const width = ctx.measureText(text).width;
 
       ctx.fillStyle = `hsl(${hue}, 45%, 75%)`;
       ctx.strokeStyle = `hsl(${hue}, 45%, 45%)`;
@@ -187,12 +186,7 @@
       ctx.stroke();
 
       // Draw the tooltip.
-      ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
-      ctx.fillRect(this.mouseXpos + 5, MARGIN_TOP, width + 16, RECT_HEIGHT);
-      ctx.fillStyle = 'hsl(200, 50%, 40%)';
-      ctx.textAlign = 'left';
-      ctx.textBaseline = 'middle';
-      ctx.fillText(text, this.mouseXpos + 8, MARGIN_TOP + RECT_HEIGHT / 2);
+      this.drawTrackHoverTooltip(ctx, this.mouseXpos, text);
     }
 
     // Write the Y scale on the top left corner.
@@ -203,6 +197,19 @@
     ctx.textBaseline = 'alphabetic';
     ctx.fillText(`${yLabel}`, 5, 14);
 
+    // TODO(hjd): Refactor this into checkerboardExcept
+    {
+      const endPx = timeScale.timeToPx(visibleWindowTime.end);
+      const counterEndPx =
+          Math.min(timeScale.timeToPx(this.config.endTs || Infinity), endPx);
+
+      // Grey out RHS.
+      if (counterEndPx < endPx) {
+        ctx.fillStyle = '#0000001f';
+        ctx.fillRect(counterEndPx, 0, endPx - counterEndPx, this.getHeight());
+      }
+    }
+
     // If the cached trace slices don't fully cover the visible time range,
     // show a gray rectangle with a "Loading..." label.
     checkerboardExcept(
@@ -242,6 +249,7 @@
       return false;
     } else {
       const counterId = data.ids[left];
+      if (counterId === -1) return true;
       globals.makeSelection(Actions.selectCounter({
         leftTs: toNs(data.timestamps[left]),
         rightTs: right !== -1 ? toNs(data.timestamps[right]) : -1,
diff --git a/ui/src/tracks/cpu_freq/controller.ts b/ui/src/tracks/cpu_freq/controller.ts
index bdfbdae..9e001bb 100644
--- a/ui/src/tracks/cpu_freq/controller.ts
+++ b/ui/src/tracks/cpu_freq/controller.ts
@@ -49,9 +49,9 @@
       await this.query(`create view ${this.tableName('freq')}
           as select
             ts,
-            lead(ts) over () - ts as dur,
+            dur,
             value as freq_value
-          from counter c
+          from experimental_counter_dur c
           where track_id = ${this.config.freqTrackId};
       `);
 
@@ -68,9 +68,9 @@
         await this.query(`create view ${this.tableName('idle')}
           as select
             ts,
-            lead(ts) over () - ts as dur,
+            dur,
             value as idle_value
-          from counter c
+          from experimental_counter_dur c
           where track_id = ${this.config.idleTrackId};
         `);
       }
diff --git a/ui/src/tracks/cpu_freq/frontend.ts b/ui/src/tracks/cpu_freq/frontend.ts
index 2554521..77ba9b1 100644
--- a/ui/src/tracks/cpu_freq/frontend.ts
+++ b/ui/src/tracks/cpu_freq/frontend.ts
@@ -162,14 +162,9 @@
         // Display the idle value +1 to be consistent with catapult.
         text += ` (Idle: ${(this.hoveredIdle + 1).toLocaleString()})`;
       }
-      const width = ctx.measureText(text).width;
 
       // Draw the tooltip.
-      ctx.textBaseline = 'middle';
-      ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
-      ctx.fillRect(this.mouseXpos, MARGIN_TOP, width + 16, RECT_HEIGHT);
-      ctx.fillStyle = 'hsl(200, 50%, 40%)';
-      ctx.fillText(text, this.mouseXpos + 8, MARGIN_TOP + RECT_HEIGHT / 2);
+      this.drawTrackHoverTooltip(ctx, this.mouseXpos, text);
     }
 
     // Write the Y scale on the top left corner.
diff --git a/ui/src/tracks/cpu_slices/controller.ts b/ui/src/tracks/cpu_slices/controller.ts
index 278450e..ec34dc2 100644
--- a/ui/src/tracks/cpu_slices/controller.ts
+++ b/ui/src/tracks/cpu_slices/controller.ts
@@ -109,7 +109,7 @@
     // TODO(hjd): Remove LIMIT
     const LIMIT = 10000;
 
-    const query = `select ts,dur,utid,row_id from ${this.tableName('span')}
+    const query = `select ts,dur,utid,id from ${this.tableName('span')}
         where cpu = ${this.config.cpu}
         and utid != 0
         limit ${LIMIT};`;
diff --git a/ui/src/tracks/cpu_slices/frontend.ts b/ui/src/tracks/cpu_slices/frontend.ts
index d104fe9..5edcc3f 100644
--- a/ui/src/tracks/cpu_slices/frontend.ts
+++ b/ui/src/tracks/cpu_slices/frontend.ts
@@ -124,9 +124,7 @@
       }
       const rectStart = timeScale.timeToPx(tStart);
       const rectEnd = timeScale.timeToPx(tEnd);
-      const rectWidth = rectEnd - rectStart;
-      if (rectWidth < 0.3) continue;
-
+      const rectWidth = Math.max(1, rectEnd - rectStart);
       const threadInfo = globals.threads.get(utid);
 
       // TODO: consider de-duplicating this code with the copied one from
@@ -162,7 +160,7 @@
         color.s -= 20;
       }
       ctx.fillStyle = `hsl(${color.h}, ${color.s}%, ${color.l}%)`;
-      ctx.fillRect(rectStart, MARGIN_TOP, rectEnd - rectStart, RECT_HEIGHT);
+      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;
@@ -189,12 +187,13 @@
         const color = colorForThread(globals.threads.get(utid));
         const rectStart = timeScale.timeToPx(tStart);
         const rectEnd = timeScale.timeToPx(tEnd);
+        const rectWidth = Math.max(1, rectEnd - rectStart);
+
         // Draw a rectangle around the slice that is currently selected.
         ctx.strokeStyle = `hsl(${color.h}, ${color.s}%, 30%)`;
         ctx.beginPath();
         ctx.lineWidth = 3;
-        ctx.strokeRect(
-            rectStart, MARGIN_TOP - 1.5, rectEnd - rectStart, RECT_HEIGHT + 3);
+        ctx.strokeRect(rectStart, MARGIN_TOP - 1.5, rectWidth, RECT_HEIGHT + 3);
         ctx.closePath();
         // Draw arrow from wakeup time of current slice.
         if (details.wakeupTs) {
@@ -241,29 +240,14 @@
     }
 
     const hoveredThread = globals.threads.get(this.utidHoveredInThisTrack);
-    if (hoveredThread !== undefined) {
-      let line1 = '';
-      let line2 = '';
+    if (hoveredThread !== undefined && this.mouseXpos !== undefined) {
+      const tidText = `T: ${hoveredThread.threadName} [${hoveredThread.tid}]`;
       if (hoveredThread.pid) {
-        line1 = `P: ${hoveredThread.procName} [${hoveredThread.pid}]`;
-        line2 = `T: ${hoveredThread.threadName} [${hoveredThread.tid}]`;
+        const pidText = `P: ${hoveredThread.procName} [${hoveredThread.pid}]`;
+        this.drawTrackHoverTooltip(ctx, this.mouseXpos, pidText, tidText);
       } else {
-        line1 = `T: ${hoveredThread.threadName} [${hoveredThread.tid}]`;
+        this.drawTrackHoverTooltip(ctx, this.mouseXpos, tidText);
       }
-
-      ctx.font = '10px Roboto Condensed';
-      const line1Width = ctx.measureText(line1).width;
-      const line2Width = ctx.measureText(line2).width;
-      const width = Math.max(line1Width, line2Width);
-
-      ctx.fillStyle = 'rgba(255, 255, 255, 0.9)';
-      ctx.fillRect(this.mouseXpos!, MARGIN_TOP, width + 16, RECT_HEIGHT);
-      ctx.fillStyle = 'hsl(200, 50%, 40%)';
-      ctx.textAlign = 'left';
-      ctx.fillText(
-          line1, this.mouseXpos! + 8, MARGIN_TOP + RECT_HEIGHT / 2 - 2);
-      ctx.fillText(
-          line2, this.mouseXpos! + 8, MARGIN_TOP + RECT_HEIGHT / 2 + 10);
     }
   }
 
diff --git a/ui/src/tracks/gpu_freq/frontend.ts b/ui/src/tracks/gpu_freq/frontend.ts
index 5994cf6..7f42c70 100644
--- a/ui/src/tracks/gpu_freq/frontend.ts
+++ b/ui/src/tracks/gpu_freq/frontend.ts
@@ -114,7 +114,6 @@
         text = `Weighted avg freq: ${this.hoveredValue.toLocaleString()}kHz`;
       }
 
-      const width = ctx.measureText(text).width;
       ctx.fillStyle = `hsl(${hue}, 45%, 75%)`;
       ctx.strokeStyle = `hsl(${hue}, 45%, 45%)`;
 
@@ -139,11 +138,7 @@
       ctx.stroke();
 
       // Draw the tooltip.
-      ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
-      ctx.fillRect(this.mouseXpos + 5, MARGIN_TOP, width + 16, RECT_HEIGHT);
-      ctx.fillStyle = 'hsl(200, 50%, 40%)';
-      const centerY = MARGIN_TOP + RECT_HEIGHT / 2;
-      ctx.fillText(text, this.mouseXpos + 10, centerY - 3);
+      this.drawTrackHoverTooltip(ctx, this.mouseXpos, text);
     }
 
     // Write the Y scale on the top left corner.
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/process_scheduling/controller.ts b/ui/src/tracks/process_scheduling/controller.ts
index 9d1be1c..3736cca 100644
--- a/ui/src/tracks/process_scheduling/controller.ts
+++ b/ui/src/tracks/process_scheduling/controller.ts
@@ -53,7 +53,7 @@
           select utid, upid from thread
             where utid != 0 and upid = ${this.config.upid}) using(utid);`);
       await this.query(`create virtual table ${this.tableName('span')}
-              using span_join(${this.tableName('process')} PARTITIONED cpu,
+              using span_join(${this.tableName('process')} PARTITIONED utid,
                               ${this.tableName('window')});`);
       this.maxCpu = Math.max(...await this.engine.getCpus()) + 1;
       this.setup = true;
@@ -89,15 +89,10 @@
     const endNs = toNs(end);
     const numBuckets = Math.ceil((endNs - startNs) / bucketSizeNs);
 
-    // cpu < maxCpu improves performance a lot since the window table can
-    // avoid generating many rows.
     const query = `select
         quantum_ts as bucket,
         sum(dur)/cast(${bucketSizeNs * this.maxCpu} as float) as utilization
         from ${this.tableName('span')}
-        where upid = ${this.config.upid}
-        and utid != 0
-        and cpu < ${this.maxCpu}
         group by quantum_ts
         limit ${LIMIT}`;
 
@@ -123,17 +118,8 @@
 
   private async computeSlices(start: number, end: number, resolution: number):
       Promise<SliceData> {
-    // cpu < maxCpu improves performance a lot since the window table can
-    // avoid generating many rows.
     const query = `select ts,dur,cpu,utid from ${this.tableName('span')}
-        join
-        (select utid from thread where upid = ${this.config.upid})
-        using(utid)
-        where
-        cpu < ${this.maxCpu}
-        order by
-        cpu,
-        ts
+        order by cpu, ts
         limit ${LIMIT};`;
     const rawResult = await this.query(query);
 
diff --git a/ui/src/tracks/process_scheduling/frontend.ts b/ui/src/tracks/process_scheduling/frontend.ts
index 9bc4f20..5246124 100644
--- a/ui/src/tracks/process_scheduling/frontend.ts
+++ b/ui/src/tracks/process_scheduling/frontend.ts
@@ -147,27 +147,14 @@
     }
 
     const hoveredThread = globals.threads.get(this.utidHoveredInThisTrack);
-    if (hoveredThread !== undefined) {
-      let line1 = '';
-      let line2 = '';
+    if (hoveredThread !== undefined && this.mouseXpos !== undefined) {
+      const tidText = `T: ${hoveredThread.threadName} [${hoveredThread.tid}]`;
       if (hoveredThread.pid) {
-        line1 = `P: ${hoveredThread.procName} [${hoveredThread.pid}]`;
-        line2 = `T: ${hoveredThread.threadName} [${hoveredThread.tid}]`;
+        const pidText = `P: ${hoveredThread.procName} [${hoveredThread.pid}]`;
+        this.drawTrackHoverTooltip(ctx, this.mouseXpos, pidText, tidText);
       } else {
-        line1 = `T: ${hoveredThread.threadName} [${hoveredThread.tid}]`;
+        this.drawTrackHoverTooltip(ctx, this.mouseXpos, tidText);
       }
-
-      ctx.font = '10px Roboto Condensed';
-      const line1Width = ctx.measureText(line1).width;
-      const line2Width = ctx.measureText(line2).width;
-      const width = Math.max(line1Width, line2Width);
-
-      ctx.fillStyle = 'rgba(255, 255, 255, 0.9)';
-      ctx.fillRect(this.mouseXpos!, MARGIN_TOP, width + 16, RECT_HEIGHT);
-      ctx.fillStyle = 'hsl(200, 50%, 40%)';
-      ctx.textAlign = 'left';
-      ctx.fillText(line1, this.mouseXpos! + 8, 18);
-      ctx.fillText(line2, this.mouseXpos! + 8, 28);
     }
   }
 
diff --git a/ui/src/tracks/process_summary/controller.ts b/ui/src/tracks/process_summary/controller.ts
index e9f5b4b..d226ace 100644
--- a/ui/src/tracks/process_summary/controller.ts
+++ b/ui/src/tracks/process_summary/controller.ts
@@ -49,16 +49,18 @@
         utids = threadQuery.columns[0].longValues! as number[];
       }
 
+      const trackQuery = await this.query(
+          `select id from thread_track where utid in (${utids.join(',')})`);
+      const tracks = trackQuery.columns[0].longValues! as number[];
+
       const processSliceView = this.tableName('process_slice_view');
       await this.query(
           `create view ${processSliceView} as ` +
           // 0 as cpu is a dummy column to perform span join on.
           `select ts, dur/${utids.length} as dur ` +
           `from slice s ` +
-          `inner join thread_track t on s.track_id = t.id ` +
-          `where depth = 0 and utid in ` +
-          // TODO(dproy): This query is faster if we write it as x < utid < y.
-          `(${utids.join(',')})`);
+          `where depth = 0 and track_id in ` +
+          `(${tracks.join(',')})`);
       await this.query(`create virtual table ${this.tableName('span')}
           using span_join(${processSliceView},
                           ${this.tableName('window')});`);
diff --git a/ui/src/tracks/thread_state/common.ts b/ui/src/tracks/thread_state/common.ts
index 7a72f3f..6c21344 100644
--- a/ui/src/tracks/thread_state/common.ts
+++ b/ui/src/tracks/thread_state/common.ts
@@ -22,10 +22,11 @@
   ends: Float64Array;
   state: Uint16Array;  // Index into |strings|.
   cpu: Uint8Array;
+  summarisedStateBreakdowns: Map<number, StatePercent>;
 }
 
-export interface Config { utid: number; }
+export type StatePercent = Map<string, number>;
 
-export function groupBusyStates(resolution: number) {
-  return resolution >= 0.0001;
+export interface Config {
+  utid: number;
 }
diff --git a/ui/src/tracks/thread_state/controller.ts b/ui/src/tracks/thread_state/controller.ts
index 2c90c0d..521da7e 100644
--- a/ui/src/tracks/thread_state/controller.ts
+++ b/ui/src/tracks/thread_state/controller.ts
@@ -23,7 +23,6 @@
 import {
   Config,
   Data,
-  groupBusyStates,
   THREAD_STATE_TRACK_KIND,
 } from './common';
 
@@ -35,82 +34,59 @@
       Promise<Data> {
     const startNs = toNs(start);
     const endNs = toNs(end);
-    let minNs = 0;
-    if (groupBusyStates(resolution)) {
-      // Ns for 1px (the smallest state to display)
-      minNs = Math.round(resolution * 1e9);
-    }
+    const minNs = Math.round(resolution * 1e9);
 
-    if (this.setup === false) {
-      await this.query(
-          `create virtual table ${this.tableName('window')} using window;`);
+    await this.query(
+        `drop view if exists ${this.tableName('grouped_thread_states')}`);
 
-      await this.query(`create view ${this.tableName('long_states')} as
-      select * from thread_state where dur >= ${minNs} and utid = ${
-          this.config.utid}`);
+    // This query gives all contiguous slices less than minNs the same grouping.
+    await this.query(`create view ${this.tableName('grouped_thread_states')} as
+    select ts, dur, cpu, state,
+    ifnull(sum(value) over (order by ts), 0) as grouping
+    from
+    (select *,
+    (dur >= ${minNs}) or lag(dur >= ${minNs}) over (order by ts) as value
+    from thread_state
+    where utid = ${this.config.utid})`);
 
-      // Create a slice from the first ts to the end of the trace. To
-      // be span joined with the long states - This effectively combines all
-      // of the short states into a single 'Busy' state.
-      await this.query(`create view ${this.tableName('fill_gaps')} as select
-      (select min(ts) from thread_state where utid = ${this.config.utid}) as ts,
-      (select end_ts from trace_bounds) -
-      (select min(ts) from thread_state where utid = ${
-          this.config.utid}) as dur,
-      ${this.config.utid} as utid`);
+    // Since there are more rows than slices we will output, check the number of
+    // distinct groupings to find the number of slices.
+    const totalSlicesQuery = `select count(distinct(grouping))
+      from ${this.tableName('grouped_thread_states')}
+      where ts <= ${endNs} and ts + dur >= ${startNs}`;
+    const totalSlices = (await this.engine.queryOneRow(totalSlicesQuery))[0];
 
-      await this.query(`create virtual table ${this.tableName('summarized')}
-      using span_left_join(${this.tableName('fill_gaps')} partitioned utid,
-      ${this.tableName('long_states')} partitioned utid)`);
-
-      await this.query(`create virtual table ${this.tableName('current')}
-      using span_join(
-        ${this.tableName('window')},
-        ${this.tableName('summarized')} partitioned utid)`);
-
-      this.setup = true;
-    }
-
-    const windowDurNs = Math.max(1, endNs - startNs);
-
-    this.query(`update ${this.tableName('window')} set
-     window_start=${startNs},
-     window_dur=${windowDurNs},
-     quantum=0`);
-
-    this.query(`drop view if exists ${this.tableName('long_states')}`);
-    this.query(`drop view if exists ${this.tableName('fill_gaps')}`);
-
-    await this.query(`create view ${this.tableName('long_states')} as
-      select * from thread_state where dur >= ${minNs} and utid = ${
-        this.config.utid}`);
-
-    await this.query(`create view ${this.tableName('fill_gaps')} as select
-      (select min(ts) from thread_state where utid = ${this.config.utid}) as ts,
-      (select end_ts from trace_bounds) -
-      (select min(ts) from thread_state where utid = ${
-        this.config.utid}) as dur,
-      ${this.config.utid} as utid`);
-
-    const query = `select ts, cast(dur as double), utid,
-    case when state is not null then state else 'Busy' end as state,
-    cast(cpu as double)
-    from ${this.tableName('current')} limit ${LIMIT}`;
+    // We have ts contraints instead of span joining with the window table
+    // because when selecting a slice we need the real duration (even if it
+    // is outside of the current viewport)
+    // TODO(b/149303809): Return this to using
+    // the window table if possible.
+    const query = `select min(min(ts)) over (partition by grouping) as ts,
+    sum(sum(dur)) over (partition by grouping) as slice_dur,
+    cpu,
+    state,
+    (sum(dur) * 1.0)/(sum(sum(dur)) over (partition by grouping)) as percent,
+    grouping
+    from ${this.tableName('grouped_thread_states')}
+    where ts <= ${endNs} and ts + dur >= ${startNs}
+    group by grouping, state
+    order by grouping
+    limit ${LIMIT}`;
 
     const result = await this.query(query);
-
     const numRows = +result.numRecords;
 
     const summary: Data = {
       start,
       end,
       resolution,
-      length: numRows,
-      starts: new Float64Array(numRows),
-      ends: new Float64Array(numRows),
+      length: totalSlices,
+      starts: new Float64Array(totalSlices),
+      ends: new Float64Array(totalSlices),
       strings: [],
-      state: new Uint16Array(numRows),
-      cpu: new Uint8Array(numRows)
+      state: new Uint16Array(totalSlices),
+      cpu: new Uint8Array(totalSlices),
+      summarisedStateBreakdowns: new Map(),
     };
 
     const stringIndexes = new Map<string, number>();
@@ -123,25 +99,52 @@
       return idx;
     }
 
+    let outIndex = 0;
     for (let row = 0; row < numRows; row++) {
       const cols = result.columns;
-      const start = fromNs(+cols[0].longValues![row]);
-      summary.starts[row] = start;
-      summary.ends[row] = start + fromNs(+cols[1].doubleValues![row]);
-      summary.state[row] = internString(cols[3].stringValues![row]);
-      summary.cpu[row] = +cols[4].doubleValues![row];
-    }
+      const start = +cols[0].longValues![row];
+      const dur = +cols[1].longValues![row];
+      const state = cols[3].stringValues![row];
+      const percent = +(cols[4].doubleValues![row].toFixed(2));
+      const grouping = +cols[5].longValues![row];
 
+      if (percent !== 1) {
+        let breakdownMap = summary.summarisedStateBreakdowns.get(outIndex);
+        if (!breakdownMap) {
+          breakdownMap = new Map();
+          summary.summarisedStateBreakdowns.set(outIndex, breakdownMap);
+        }
+
+        const currentPercent = breakdownMap.get(state);
+        if (currentPercent === undefined) {
+          breakdownMap.set(state, percent);
+        } else {
+          breakdownMap.set(state, currentPercent + percent);
+        }
+      }
+
+      const nextGrouping =
+          row + 1 < numRows ? +cols[5].longValues![row + 1] : -1;
+      // If the next grouping is different then we have reached the end of this
+      // slice.
+      if (grouping !== nextGrouping) {
+        const numStates = summary.summarisedStateBreakdowns.get(outIndex) ?
+            summary.summarisedStateBreakdowns.get(outIndex)!.entries.length :
+            1;
+        summary.starts[outIndex] = fromNs(start);
+        summary.ends[outIndex] = fromNs(start + dur);
+        summary.state[outIndex] =
+            internString(numStates === 1 ? state : 'Various states');
+        summary.cpu[outIndex] = +cols[2].longValues![row];
+        outIndex++;
+      }
+    }
     return summary;
   }
 
   onDestroy(): void {
     if (this.setup) {
-      this.query(`drop table ${this.tableName('window')}`);
-      this.query(`drop table ${this.tableName('current')}`);
-      this.query(`drop table ${this.tableName('summarized')}`);
-      this.query(`drop view ${this.tableName('long_states')}`);
-      this.query(`drop view ${this.tableName('fill_gaps')}`);
+      this.query(`drop view ${this.tableName('grouped_thread_states')}`);
       this.setup = false;
     }
   }
diff --git a/ui/src/tracks/thread_state/frontend.ts b/ui/src/tracks/thread_state/frontend.ts
index cae38f2..91b54ee 100644
--- a/ui/src/tracks/thread_state/frontend.ts
+++ b/ui/src/tracks/thread_state/frontend.ts
@@ -12,13 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-
 import {search, searchEq} from '../../base/binary_search';
 import {Actions} from '../../common/actions';
 import {cropText} from '../../common/canvas_utils';
 import {TrackState} from '../../common/state';
 import {translateState} from '../../common/thread_state';
-import {colorForState} from '../../frontend/colorizer';
+import {checkerboardExcept} from '../../frontend/checkerboard';
+import {Color, colorForState} from '../../frontend/colorizer';
 import {globals} from '../../frontend/globals';
 import {Track} from '../../frontend/track';
 import {trackRegistry} from '../../frontend/track_registry';
@@ -26,12 +26,27 @@
 import {
   Config,
   Data,
-  groupBusyStates,
+  StatePercent,
   THREAD_STATE_TRACK_KIND,
 } from './common';
 
 const MARGIN_TOP = 4;
 const RECT_HEIGHT = 14;
+const EXCESS_WIDTH = 10;
+
+function groupBusyStates(resolution: number) {
+  return resolution >= 0.0001;
+}
+
+function getSummarizedSliceText(breakdownMap: StatePercent) {
+  let result = 'Various (';
+  const sorted =
+      new Map([...breakdownMap.entries()].sort((a, b) => b[1] - a[1]));
+  for (const [state, value] of sorted.entries()) {
+    result += `${state}: ${Math.round(value * 100)}%, `;
+  }
+  return result.slice(0, result.length - 2) + ')';
+}
 
 class ThreadStateTrack extends Track<Config, Data> {
   static readonly kind = THREAD_STATE_TRACK_KIND;
@@ -54,6 +69,15 @@
 
     if (data === undefined) return;  // Can't possibly draw anything.
 
+    checkerboardExcept(
+        ctx,
+        this.getHeight(),
+        timeScale.timeToPx(visibleWindowTime.start),
+        timeScale.timeToPx(visibleWindowTime.end),
+        timeScale.timeToPx(data.start),
+        timeScale.timeToPx(data.end),
+    );
+
     const shouldGroupBusyStates = groupBusyStates(data.resolution);
 
     ctx.textAlign = 'center';
@@ -71,9 +95,16 @@
         if (state === 'x') continue;
         const rectStart = timeScale.timeToPx(tStart);
         const rectEnd = timeScale.timeToPx(tEnd);
-        const color = colorForState(state);
-        ctx.fillStyle = `hsl(${color.h},${color.s}%,${color.l}%)`;
         let rectWidth = rectEnd - rectStart;
+        const color = colorForState(state);
+        let text = translateState(state);
+        const breakdown = data.summarisedStateBreakdowns.get(i);
+        if (breakdown) {
+          colorSummarizedSlice(breakdown, rectStart, rectEnd);
+          text = getSummarizedSliceText(breakdown);
+        } else {
+          ctx.fillStyle = `hsl(${color.h},${color.s}%,${color.l}%)`;
+        }
         if (shouldGroupBusyStates && rectWidth < 1) {
           rectWidth = 1;
         }
@@ -81,9 +112,9 @@
 
         // 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 title = cropText(text, charWidth, rectWidth);
         const rectXCenter = rectStart + rectWidth / 2;
-        ctx.fillStyle = color.l < 80 ? '#fff' : '#404040';
+        ctx.fillStyle = color.l > 80 || breakdown ? '#404040' : '#fff';
         ctx.fillText(title, rectXCenter, MARGIN_TOP + RECT_HEIGHT / 2 + 3);
       }
     }
@@ -96,8 +127,16 @@
         const tStart = data.starts[startIndex];
         const tEnd = data.ends[startIndex];
         const state = data.strings[data.state[startIndex]];
-        const rectStart = timeScale.timeToPx(tStart);
-        const rectEnd = timeScale.timeToPx(tEnd);
+
+        // If we try to draw too far off the end of the canvas (+/-4m~),
+        // the line is not drawn. Instead limit drawing to the canvas
+        // boundaries, but allow some excess to ensure that the start and end
+        // of the rect are not shown unless that is truly when it starts/ends.
+        const rectStart =
+            Math.max(0 - EXCESS_WIDTH, timeScale.timeToPx(tStart));
+        const rectEnd = Math.min(
+            timeScale.timeToPx(visibleWindowTime.end) + EXCESS_WIDTH,
+            timeScale.timeToPx(tEnd));
         const color = colorForState(state);
         ctx.strokeStyle = `hsl(${color.h},${color.s}%,${color.l * 0.7}%)`;
         ctx.beginPath();
@@ -107,6 +146,35 @@
         ctx.closePath();
       }
     }
+
+    // Make a gradient ordered most common to least based on the colors of the
+    // states within the summarized slice.
+    function colorSummarizedSlice(
+        breakdownMap: StatePercent, rectStart: number, rectEnd: number) {
+      const gradient =
+          ctx.createLinearGradient(rectStart, MARGIN_TOP, rectEnd, MARGIN_TOP);
+      // Sometimes multiple states have the same color e.g R, R+
+      const colorMap = new Map<Color, number>();
+      for (const [state, value] of breakdownMap.entries()) {
+        const color = colorForState(state);
+        const currentColorValue = colorMap.get(color);
+        if (currentColorValue === undefined) {
+          colorMap.set(color, value);
+        } else {
+          colorMap.set(color, currentColorValue + value);
+        }
+      }
+
+      const sorted =
+          new Map([...colorMap.entries()].sort((a, b) => b[1] - a[1]));
+      let colorStop = 0;
+      for (const [color, value] of sorted.entries()) {
+        const colorString = `hsl(${color.h},${color.s}%,${color.l}%)`;
+        colorStop = Math.max(0, Math.min(1, colorStop + value));
+        gradient.addColorStop(colorStop, colorString);
+      }
+      ctx.fillStyle = gradient;
+    }
   }
 
   onMouseClick({x}: {x: number}) {